From: Ximin Luo Date: Sat, 3 Mar 2018 11:56:08 +0000 (+0100) Subject: Merge remote-tracking branch 'upstream/master' into pr4988 X-Git-Tag: archive/raspbian/0.35.0-2+rpi1~3^2^2^2^2^2^2^2~22^2~2^2~56^2~3 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/success//%22http:/www.example.com/cgi/success/?a=commitdiff_plain;h=9c5eecd41900686a1fee27a3f6ebd6eecfa14e36;p=cargo.git Merge remote-tracking branch 'upstream/master' into pr4988 --- 9c5eecd41900686a1fee27a3f6ebd6eecfa14e36 diff --cc src/cargo/core/resolver/mod.rs index 7b33cba2c,54e8517f4..11a81e3fc --- a/src/cargo/core/resolver/mod.rs +++ b/src/cargo/core/resolver/mod.rs @@@ -722,36 -766,73 +778,74 @@@ fn activate_deps_loop<'a> // find a dependency that does have a candidate to try, and try // to activate that one. This resets the `remaining_deps` to // their state at the found level of the `backtrack_stack`. - trace!("{}[{}]>{} -- no candidates", parent.name(), cur, - dep.name()); - match find_candidate(&mut backtrack_stack, - &mut cx, - &mut remaining_deps, - &mut parent, - &mut cur, - &mut dep, - &mut features) { - None => return Err(activation_error(&cx, registry, &parent, - &dep, - cx.prev_active(&dep), - &candidates, config)), - Some(candidate) => candidate, + trace!("{}[{}]>{} -- no candidates", parent.name(), cur, dep.name()); + find_candidate( + &mut backtrack_stack, + &mut cx, + &mut remaining_deps, + &mut parent, + &mut cur, + &mut dep, + &mut features, + &mut remaining_candidates, + &mut conflicting_activations, + ).ok_or_else(|| { + activation_error( + &cx, + registry, + &parent, + &dep, + &conflicting_activations, + &candidates, + config, + ) + }) + })?; + + // We have a candidate. Clone a `BacktrackFrame` + // so we can add it to the `backtrack_stack` if activation succeeds. + // We clone now in case `activate` changes `cx` and then fails. + let backtrack = BacktrackFrame { + cur, + context_backup: Context::clone(&cx), + deps_backup: >::clone(&remaining_deps), + remaining_candidates: remaining_candidates.clone(), + parent: Summary::clone(&parent), + dep: Dependency::clone(&dep), + features: Rc::clone(&features), + conflicting_activations: conflicting_activations.clone(), + }; + + let method = Method::Required { + dev_deps: false, + features: &features, ++ all_features: false, + uses_default_features: dep.uses_default_features(), + }; + trace!("{}[{}]>{} trying {}", parent.name(), cur, dep.name(), candidate.summary.version()); + let res = activate(&mut cx, registry, Some(&parent), candidate, &method); + successfully_activated = res.is_ok(); + + match res { + Ok(Some((frame, dur))) => { + remaining_deps.push(frame); + deps_time += dur; } + Ok(None) => (), + Err(ActivateError::Error(e)) => return Err(e), + Err(ActivateError::Conflict(id, reason)) => { conflicting_activations.insert(id, reason); }, } - }; - let method = Method::Required { - dev_deps: false, - features: &features, - all_features: false, - uses_default_features: dep.uses_default_features(), - }; - trace!("{}[{}]>{} trying {}", parent.name(), cur, dep.name(), - candidate.summary.version()); - let res = activate(&mut cx, registry, Some(&parent), candidate, &method)?; - if let Some((frame, dur)) = res { - remaining_deps.push(frame); - deps_time += dur; + // Add an entry to the `backtrack_stack` so + // we can try the next one if this one fails. + if successfully_activated { + if has_another { + backtrack_stack.push(backtrack); + } + } else { + // `activate` changed `cx` and then failed so put things back. + cx = backtrack.context_backup; + } } } @@@ -1066,8 -1201,6 +1215,8 @@@ impl<'a> Context<'a> } debug!("checking if {} is already activated", summary.package_id()); let (features, use_default) = match *method { + Method::Everything | - Method::Required { all_features: true, .. } => return false, ++ Method::Required { all_features: true, .. } => return Ok(false), Method::Required { features, uses_default_features, .. } => { (features, uses_default_features) } diff --cc src/cargo/ops/cargo_compile.rs index d7c08c9c4,34cc9d63e..4d343b230 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@@ -226,26 -233,14 +233,20 @@@ pub fn compile_ws<'a>(ws: &Workspace<'a let profiles = ws.profiles(); let specs = spec.into_package_id_specs(ws)?; - let resolve = ops::resolve_ws_precisely(ws, - source, - features, - all_features, - no_default_features, - &specs)?; + let features = Method::split_features(features); + let method = Method::Required { + dev_deps: ws.require_optional_deps() || filter.need_dev_deps(), + features: &features, + all_features, + uses_default_features: !no_default_features, + }; + let resolve = ops::resolve_ws_with_method(ws, + source, + method, + &specs, + )?; let (packages, resolve_with_overrides) = resolve; - if specs.is_empty() { - bail!("manifest path `{}` contains no package: The manifest is virtual, \ - and the workspace has no members.", - ws.current_manifest().display()) - } - let to_builds = specs.iter().map(|p| { let pkgid = p.query(resolve_with_overrides.iter())?; let p = packages.get(pkgid)?; diff --cc src/cargo/ops/resolve.rs index 7a2c3d48b,f6e57621a..6ade55153 --- a/src/cargo/ops/resolve.rs +++ b/src/cargo/ops/resolve.rs @@@ -77,9 -65,19 +77,9 @@@ pub fn resolve_ws_with_method<'a>(ws: & Some(resolve) } else { - None + ops::load_pkg_lockfile(ws)? }; - let method = if all_features { - Method::Everything - } else { - Method::Required { - dev_deps: true, // TODO: remove this option? - features: &features, - uses_default_features: !no_default_features, - } - }; - let resolved_with_overrides = ops::resolve_with_previous(&mut registry, ws, @@@ -236,9 -234,8 +236,9 @@@ pub fn resolve_with_previous<'a>(regist // for any other packages specified with `-p`. Method::Required { dev_deps, .. } => { let base = Method::Required { - dev_deps: dev_deps, + dev_deps, features: &[], + all_features: false, uses_default_features: true, }; let member_id = member.package_id(); diff --cc tests/testsuite/build.rs index 000000000,51b449af5..e4237d864 mode 000000,100644..100644 --- a/tests/testsuite/build.rs +++ b/tests/testsuite/build.rs @@@ -1,0 -1,4292 +1,4313 @@@ + use std::env; + use std::fs::{self, File}; + use std::io::prelude::*; + + use cargo::util::paths::dylib_path_envvar; + use cargo::util::{process, ProcessBuilder}; + use cargotest::{is_nightly, rustc_host, sleep_ms}; + use cargotest::support::paths::{CargoPathExt,root}; + use cargotest::support::{ProjectBuilder}; + use cargotest::support::{project, execs, main_file, basic_bin_manifest}; + use cargotest::support::registry::Package; + use cargotest::ChannelChanger; + use hamcrest::{assert_that, existing_file, existing_dir, is_not}; + use tempdir::TempDir; + + #[test] + fn cargo_compile_simple() { + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + assert_that(&p.bin("foo"), existing_file()); + + assert_that(process(&p.bin("foo")), + execs().with_status(0).with_stdout("i am foo\n")); + } + + #[test] + fn cargo_fail_with_no_stderr() { + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &String::from("refusal")) + .build(); + assert_that(p.cargo("build").arg("--message-format=json"), execs().with_status(101) + .with_stderr_does_not_contain("--- stderr")); + } + + /// Check that the `CARGO_INCREMENTAL` environment variable results in + /// `rustc` getting `-Zincremental` passed to it. + #[test] + fn cargo_compile_incremental() { + if !is_nightly() { + return + } + + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + assert_that( + p.cargo("build").arg("-v").env("CARGO_INCREMENTAL", "1"), + execs().with_stderr_contains( + "[RUNNING] `rustc [..] -C incremental=[..][/]target[/]debug[/]incremental[..]`\n") + .with_status(0)); + + assert_that( + p.cargo("test").arg("-v").env("CARGO_INCREMENTAL", "1"), + execs().with_stderr_contains( + "[RUNNING] `rustc [..] -C incremental=[..][/]target[/]debug[/]incremental[..]`\n") + .with_status(0)); + } + + #[test] + fn incremental_profile() { + if !is_nightly() { + return + } + + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [profile.dev] + incremental = false + + [profile.release] + incremental = true + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that( + p.cargo("build").arg("-v").env_remove("CARGO_INCREMENTAL"), + execs().with_stderr_does_not_contain("[..]C incremental=[..]") + .with_status(0)); + + assert_that( + p.cargo("build").arg("-v").env("CARGO_INCREMENTAL", "1"), + execs().with_stderr_contains("[..]C incremental=[..]") + .with_status(0)); + + assert_that( + p.cargo("build").arg("--release").arg("-v").env_remove("CARGO_INCREMENTAL"), + execs().with_stderr_contains("[..]C incremental=[..]") + .with_status(0)); + + assert_that( + p.cargo("build").arg("--release").arg("-v").env("CARGO_INCREMENTAL", "0"), + execs().with_stderr_does_not_contain("[..]C incremental=[..]") + .with_status(0)); + } + + #[test] + fn incremental_config() { + if !is_nightly() { + return + } + + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .file(".cargo/config", r#" + [build] + incremental = false + "#) + .build(); + + assert_that( + p.cargo("build").arg("-v").env_remove("CARGO_INCREMENTAL"), + execs().with_stderr_does_not_contain("[..]C incremental=[..]") + .with_status(0)); + + assert_that( + p.cargo("build").arg("-v").env("CARGO_INCREMENTAL", "1"), + execs().with_stderr_contains("[..]C incremental=[..]") + .with_status(0)); + } + + #[test] + fn cargo_compile_with_workspace_excluded() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that( + p.cargo("build").arg("--all").arg("--exclude").arg("foo"), + execs().with_stderr_does_not_contain("[..]virtual[..]") + .with_stderr_contains("[..]no packages to compile") + .with_status(101)); + } + + #[test] + fn cargo_compile_manifest_path() { + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + assert_that(p.cargo("build") + .arg("--manifest-path").arg("foo/Cargo.toml") + .cwd(p.root().parent().unwrap()), + execs().with_status(0)); + assert_that(&p.bin("foo"), existing_file()); + } + + #[test] + fn cargo_compile_with_invalid_manifest() { + let p = project("foo") + .file("Cargo.toml", "") + .build(); + + assert_that(p.cargo("build"), + execs() + .with_status(101) + .with_stderr("\ + [ERROR] failed to parse manifest at `[..]` + + Caused by: + virtual manifests must be configured with [workspace] + ")) + } + + #[test] + fn cargo_compile_with_invalid_manifest2() { + let p = project("foo") + .file("Cargo.toml", r" + [project] + foo = bar + ") + .build(); + + assert_that(p.cargo("build"), + execs() + .with_status(101) + .with_stderr("\ + [ERROR] failed to parse manifest at `[..]` + + Caused by: + could not parse input as TOML + + Caused by: + invalid number at line 3 + ")) + } + + #[test] + fn cargo_compile_with_invalid_manifest3() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/Cargo.toml", "a = bar") + .build(); + + assert_that(p.cargo("build").arg("--manifest-path") + .arg("src/Cargo.toml"), + execs() + .with_status(101) + .with_stderr("\ + [ERROR] failed to parse manifest at `[..]` + + Caused by: + could not parse input as TOML + + Caused by: + invalid number at line 1 + ")) + } + + #[test] + fn cargo_compile_duplicate_build_targets() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "main" + path = "src/main.rs" + crate-type = ["dylib"] + + [dependencies] + "#) + .file("src/main.rs", r#" + #![allow(warnings)] + fn main() {} + "#) + .build(); + + assert_that(p.cargo("build"), + execs() + .with_status(0) + .with_stderr("\ + warning: file found to be present in multiple build targets: [..]main.rs + [COMPILING] foo v0.0.1 ([..]) + [FINISHED] [..] + ")); + } + + #[test] + fn cargo_compile_with_invalid_version() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + authors = [] + version = "1.0" + "#) + .build(); + + assert_that(p.cargo("build"), + execs() + .with_status(101) + .with_stderr("\ + [ERROR] failed to parse manifest at `[..]` + + Caused by: + Expected dot for key `project.version` + ")) + + } + + #[test] + fn cargo_compile_with_invalid_package_name() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "" + authors = [] + version = "0.0.0" + "#) + .build(); + + assert_that(p.cargo("build"), + execs() + .with_status(101) + .with_stderr("\ + [ERROR] failed to parse manifest at `[..]` + + Caused by: + package name cannot be an empty string + ")) + } + + #[test] + fn cargo_compile_with_invalid_bin_target_name() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [[bin]] + name = "" + "#) + .build(); + + assert_that(p.cargo("build"), + execs() + .with_status(101) + .with_stderr("\ + [ERROR] failed to parse manifest at `[..]` + + Caused by: + binary target names cannot be empty + ")) + } + + #[test] + fn cargo_compile_with_forbidden_bin_target_name() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [[bin]] + name = "build" + "#) + .build(); + + assert_that(p.cargo("build"), + execs() + .with_status(101) + .with_stderr("\ + [ERROR] failed to parse manifest at `[..]` + + Caused by: + the binary target name `build` is forbidden + ")) + } + + #[test] + fn cargo_compile_with_invalid_lib_target_name() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [lib] + name = "" + "#) + .build(); + + assert_that(p.cargo("build"), + execs() + .with_status(101) + .with_stderr("\ + [ERROR] failed to parse manifest at `[..]` + + Caused by: + library target names cannot be empty + ")) + } + + #[test] + fn cargo_compile_without_manifest() { + let tmpdir = TempDir::new("cargo").unwrap(); + let p = ProjectBuilder::new("foo", tmpdir.path().to_path_buf()).build(); + + assert_that(p.cargo("build"), + execs().with_status(101) + .with_stderr("\ + [ERROR] could not find `Cargo.toml` in `[..]` or any parent directory + ")); + } + + #[test] + fn cargo_compile_with_invalid_code() { + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", "invalid rust code!") + .build(); + + assert_that(p.cargo("build"), + execs() + .with_status(101) + .with_stderr_contains("\ + [ERROR] Could not compile `foo`. + + To learn more, run the command again with --verbose.\n")); + assert_that(&p.root().join("Cargo.lock"), existing_file()); + } + + #[test] + fn cargo_compile_with_invalid_code_in_deps() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "../bar" + [dependencies.baz] + path = "../baz" + "#) + .file("src/main.rs", "invalid rust code!") + .build(); + let _bar = project("bar") + .file("Cargo.toml", &basic_bin_manifest("bar")) + .file("src/lib.rs", "invalid rust code!") + .build(); + let _baz = project("baz") + .file("Cargo.toml", &basic_bin_manifest("baz")) + .file("src/lib.rs", "invalid rust code!") + .build(); + assert_that(p.cargo("build"), execs().with_status(101)); + } + + #[test] + fn cargo_compile_with_warnings_in_the_root_package() { + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", "fn main() {} fn dead() {}") + .build(); + + assert_that(p.cargo("build"), + execs().with_status(0).with_stderr_contains("\ + [..]function is never used: `dead`[..] + ")); + } + + #[test] + fn cargo_compile_with_warnings_in_a_dep_package() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = "bar" + + [[bin]] + + name = "foo" + "#) + .file("src/foo.rs", + &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file("bar/Cargo.toml", r#" + [project] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [lib] + + name = "bar" + "#) + .file("bar/src/bar.rs", r#" + pub fn gimme() -> &'static str { + "test passed" + } + + fn dead() {} + "#) + .build(); + + assert_that(p.cargo("build"), + execs().with_status(0).with_stderr_contains("\ + [..]function is never used: `dead`[..] + ")); + + assert_that(&p.bin("foo"), existing_file()); + + assert_that( + process(&p.bin("foo")), + execs().with_status(0).with_stdout("test passed\n")); + } + + #[test] + fn cargo_compile_with_nested_deps_inferred() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = 'bar' + + [[bin]] + name = "foo" + "#) + .file("src/foo.rs", + &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file("bar/Cargo.toml", r#" + [project] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.baz] + path = "../baz" + "#) + .file("bar/src/lib.rs", r#" + extern crate baz; + + pub fn gimme() -> String { + baz::gimme() + } + "#) + .file("baz/Cargo.toml", r#" + [project] + + name = "baz" + version = "0.5.0" + authors = ["wycats@example.com"] + "#) + .file("baz/src/lib.rs", r#" + pub fn gimme() -> String { + "test passed".to_string() + } + "#) + .build(); + + p.cargo("build") + .exec_with_output() + .unwrap(); + + assert_that(&p.bin("foo"), existing_file()); + assert_that(&p.bin("libbar.rlib"), is_not(existing_file())); + assert_that(&p.bin("libbaz.rlib"), is_not(existing_file())); + + assert_that( + process(&p.bin("foo")), + execs().with_status(0).with_stdout("test passed\n")); + } + + #[test] + fn cargo_compile_with_nested_deps_correct_bin() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = "bar" + + [[bin]] + name = "foo" + "#) + .file("src/main.rs", + &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file("bar/Cargo.toml", r#" + [project] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.baz] + path = "../baz" + "#) + .file("bar/src/lib.rs", r#" + extern crate baz; + + pub fn gimme() -> String { + baz::gimme() + } + "#) + .file("baz/Cargo.toml", r#" + [project] + + name = "baz" + version = "0.5.0" + authors = ["wycats@example.com"] + "#) + .file("baz/src/lib.rs", r#" + pub fn gimme() -> String { + "test passed".to_string() + } + "#) + .build(); + + p.cargo("build") + .exec_with_output() + .unwrap(); + + assert_that(&p.bin("foo"), existing_file()); + assert_that(&p.bin("libbar.rlib"), is_not(existing_file())); + assert_that(&p.bin("libbaz.rlib"), is_not(existing_file())); + + assert_that( + process(&p.bin("foo")), + execs().with_status(0).with_stdout("test passed\n")); + } + + #[test] + fn cargo_compile_with_nested_deps_shorthand() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = "bar" + "#) + .file("src/main.rs", + &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file("bar/Cargo.toml", r#" + [project] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.baz] + path = "../baz" + + [lib] + + name = "bar" + "#) + .file("bar/src/bar.rs", r#" + extern crate baz; + + pub fn gimme() -> String { + baz::gimme() + } + "#) + .file("baz/Cargo.toml", r#" + [project] + + name = "baz" + version = "0.5.0" + authors = ["wycats@example.com"] + + [lib] + + name = "baz" + "#) + .file("baz/src/baz.rs", r#" + pub fn gimme() -> String { + "test passed".to_string() + } + "#) + .build(); + + p.cargo("build") + .exec_with_output() + .unwrap(); + + assert_that(&p.bin("foo"), existing_file()); + assert_that(&p.bin("libbar.rlib"), is_not(existing_file())); + assert_that(&p.bin("libbaz.rlib"), is_not(existing_file())); + + assert_that( + process(&p.bin("foo")), + execs().with_status(0).with_stdout("test passed\n")); + } + + #[test] + fn cargo_compile_with_nested_deps_longhand() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = "bar" + version = "0.5.0" + + [[bin]] + + name = "foo" + "#) + .file("src/foo.rs", + &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file("bar/Cargo.toml", r#" + [project] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.baz] + path = "../baz" + version = "0.5.0" + + [lib] + + name = "bar" + "#) + .file("bar/src/bar.rs", r#" + extern crate baz; + + pub fn gimme() -> String { + baz::gimme() + } + "#) + .file("baz/Cargo.toml", r#" + [project] + + name = "baz" + version = "0.5.0" + authors = ["wycats@example.com"] + + [lib] + + name = "baz" + "#) + .file("baz/src/baz.rs", r#" + pub fn gimme() -> String { + "test passed".to_string() + } + "#) + .build(); + + assert_that(p.cargo("build"), execs()); + + assert_that(&p.bin("foo"), existing_file()); + assert_that(&p.bin("libbar.rlib"), is_not(existing_file())); + assert_that(&p.bin("libbaz.rlib"), is_not(existing_file())); + + assert_that(process(&p.bin("foo")), + execs().with_status(0).with_stdout("test passed\n")); + } + + // Check that Cargo gives a sensible error if a dependency can't be found + // because of a name mismatch. + #[test] + fn cargo_compile_with_dep_name_mismatch() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + + name = "foo" + version = "0.0.1" + authors = ["wycats@example.com"] + + [[bin]] + + name = "foo" + + [dependencies.notquitebar] + + path = "bar" + "#) + .file("src/bin/foo.rs", &main_file(r#""i am foo""#, &["bar"])) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/bar.rs", &main_file(r#""i am bar""#, &[])) + .build(); + + assert_that(p.cargo("build"), + execs().with_status(101).with_stderr(&format!( + r#"error: no matching package named `notquitebar` found + location searched: {proj_dir}/bar + required by package `foo v0.0.1 ({proj_dir})` + "#, proj_dir = p.url()))); + } + + #[test] + fn cargo_compile_with_filename() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", "") + .file("src/bin/a.rs", r#" + extern crate foo; + fn main() { println!("hello a.rs"); } + "#) + .file("examples/a.rs", r#" + fn main() { println!("example"); } + "#) + .build(); + + assert_that(p.cargo("build").arg("--bin").arg("bin.rs"), + execs().with_status(101).with_stderr("\ + [ERROR] no bin target named `bin.rs`")); + + assert_that(p.cargo("build").arg("--bin").arg("a.rs"), + execs().with_status(101).with_stderr("\ + [ERROR] no bin target named `a.rs` + + Did you mean `a`?")); + + assert_that(p.cargo("build").arg("--example").arg("example.rs"), + execs().with_status(101).with_stderr("\ + [ERROR] no example target named `example.rs`")); + + assert_that(p.cargo("build").arg("--example").arg("a.rs"), + execs().with_status(101).with_stderr("\ + [ERROR] no example target named `a.rs` + + Did you mean `a`?")); + } + + #[test] + fn cargo_compile_path_with_offline() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + "#) + .file("bar/src/lib.rs", "") + .build(); + + assert_that(p.cargo("build").masquerade_as_nightly_cargo().arg("-Zoffline"), + execs().with_status(0)); + } + + #[test] + fn cargo_compile_with_downloaded_dependency_with_offline() { + Package::new("present_dep", "1.2.3") + .file("Cargo.toml", r#" + [project] + name = "present_dep" + version = "1.2.3" + "#) + .file("src/lib.rs", "") + .publish(); + + { + // make package downloaded + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + + [dependencies] + present_dep = "1.2.3" + "#) + .file("src/lib.rs", "") + .build(); + assert_that(p.cargo("build"),execs().with_status(0)); + } + + let p2 = project("bar") + .file("Cargo.toml", r#" + [project] + name = "bar" + version = "0.1.0" + + [dependencies] + present_dep = "1.2.3" + "#) + .file("src/lib.rs", "") + .build(); + + assert_that(p2.cargo("build").masquerade_as_nightly_cargo().arg("-Zoffline"), + execs().with_status(0) + .with_stderr(format!("\ + [COMPILING] present_dep v1.2.3 + [COMPILING] bar v0.1.0 ([..]) + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]"))); + + } + + #[test] + fn cargo_compile_offline_not_try_update() { + let p = project("bar") + .file("Cargo.toml", r#" + [project] + name = "bar" + version = "0.1.0" + + [dependencies] + not_cached_dep = "1.2.5" + "#) + .file("src/lib.rs", "") + .build(); + + assert_that(p.cargo("build").masquerade_as_nightly_cargo().arg("-Zoffline"), + execs().with_status(101) + .with_stderr("\ + error: no matching package named `not_cached_dep` found + location searched: registry `[..]` + required by package `bar v0.1.0 ([..])` + As a reminder, you're using offline mode (-Z offline) \ + which can sometimes cause surprising resolution failures, \ + if this error is too confusing you may with to retry \ + without the offline flag.")); + } + + #[test] + fn compile_offline_without_maxvers_cached(){ + Package::new("present_dep", "1.2.1").publish(); + Package::new("present_dep", "1.2.2").publish(); + + Package::new("present_dep", "1.2.3") + .file("Cargo.toml", r#" + [project] + name = "present_dep" + version = "1.2.3" + "#) + .file("src/lib.rs", r#"pub fn get_version()->&'static str {"1.2.3"}"#) + .publish(); + + Package::new("present_dep", "1.2.5") + .file("Cargo.toml", r#" + [project] + name = "present_dep" + version = "1.2.5" + "#) + .file("src/lib.rs", r#"pub fn get_version(){"1.2.5"}"#) + .publish(); + + { + // make package cached + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + + [dependencies] + present_dep = "=1.2.3" + "#) + .file("src/lib.rs", "") + .build(); + assert_that(p.cargo("build"),execs().with_status(0)); + } + + let p2 = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + + [dependencies] + present_dep = "1.2" + "#) + .file("src/main.rs", "\ + extern crate present_dep; + fn main(){ + println!(\"{}\", present_dep::get_version()); + }") + .build(); + + assert_that(p2.cargo("run").masquerade_as_nightly_cargo().arg("-Zoffline"), + execs().with_status(0) + .with_stderr(format!("\ + [COMPILING] present_dep v1.2.3 + [COMPILING] foo v0.1.0 ({url}) + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] + Running `[..]`", url = p2.url())) + .with_stdout("1.2.3") + ); + } + + #[test] + fn incompatible_dependencies() { + Package::new("bad", "0.1.0").publish(); + Package::new("bad", "1.0.0").publish(); + Package::new("bad", "1.0.1").publish(); + Package::new("bad", "1.0.2").publish(); + Package::new("foo", "0.1.0").dep("bad", "0.1.0").publish(); + Package::new("bar", "0.1.1").dep("bad", "=1.0.0").publish(); + Package::new("bar", "0.1.0").dep("bad", "=1.0.0").publish(); + Package::new("baz", "0.1.2").dep("bad", ">=1.0.1").publish(); + Package::new("baz", "0.1.1").dep("bad", ">=1.0.1").publish(); + Package::new("baz", "0.1.0").dep("bad", ">=1.0.1").publish(); + + let p = project("transitive_load_test") + .file("Cargo.toml", r#" + [project] + name = "incompatible_dependencies" + version = "0.0.1" + + [dependencies] + foo = "0.1.0" + bar = "0.1.0" + baz = "0.1.0" + "#) + .file("src/main.rs", "fn main(){}") + .build(); + + assert_that(p.cargo("build"), + execs().with_status(101) + .with_stderr_contains("\ + error: failed to select a version for `bad`. + ... required by package `baz v0.1.0` + ... which is depended on by `incompatible_dependencies v0.0.1 ([..])` + versions that meet the requirements `>= 1.0.1` are: 1.0.2, 1.0.1 + + all possible versions conflict with previously selected packages. + + previously selected package `bad v1.0.0` + ... which is depended on by `bar v0.1.0` + ... which is depended on by `incompatible_dependencies v0.0.1 ([..])` + + failed to select a version for `bad` which could resolve this conflict")); + } + + #[test] + fn incompatible_dependencies_with_multi_semver() { + Package::new("bad", "1.0.0").publish(); + Package::new("bad", "1.0.1").publish(); + Package::new("bad", "2.0.0").publish(); + Package::new("bad", "2.0.1").publish(); + Package::new("bar", "0.1.0").dep("bad", "=1.0.0").publish(); + Package::new("baz", "0.1.0").dep("bad", ">=2.0.1").publish(); + + let p = project("transitive_load_test") + .file("Cargo.toml", r#" + [project] + name = "incompatible_dependencies" + version = "0.0.1" + + [dependencies] + bar = "0.1.0" + baz = "0.1.0" + bad = ">=1.0.1, <=2.0.0" + "#) + .file("src/main.rs", "fn main(){}") + .build(); + + assert_that(p.cargo("build"), + execs().with_status(101) + .with_stderr_contains("\ + error: failed to select a version for `bad`. + ... required by package `incompatible_dependencies v0.0.1 ([..])` + versions that meet the requirements `>= 1.0.1, <= 2.0.0` are: 2.0.0, 1.0.1 + + all possible versions conflict with previously selected packages. + + previously selected package `bad v2.0.1` + ... which is depended on by `baz v0.1.0` + ... which is depended on by `incompatible_dependencies v0.0.1 ([..])` + + previously selected package `bad v1.0.0` + ... which is depended on by `bar v0.1.0` + ... which is depended on by `incompatible_dependencies v0.0.1 ([..])` + + failed to select a version for `bad` which could resolve this conflict")); + } + + #[test] + fn compile_offline_while_transitive_dep_not_cached() { + let bar = Package::new("bar", "1.0.0"); + let bar_path = bar.archive_dst(); + bar.publish(); + + let mut content = Vec::new(); + + let mut file = File::open(bar_path.clone()).ok().unwrap(); + let _ok = file.read_to_end(&mut content).ok().unwrap(); + drop(file); + drop(File::create(bar_path.clone()).ok().unwrap() ); + + Package::new("foo", "0.1.0").dep("bar", "1.0.0").publish(); + + let p = project("transitive_load_test") + .file("Cargo.toml", r#" + [project] + name = "transitive_load_test" + version = "0.0.1" + + [dependencies] + foo = "0.1.0" + "#) + .file("src/main.rs", "fn main(){}") + .build(); + + // simulate download foo, but fail to download bar + let _out = p.cargo("build").exec_with_output(); + + drop( File::create(bar_path).ok().unwrap().write_all(&content) ); + + assert_that(p.cargo("build").masquerade_as_nightly_cargo().arg("-Zoffline"), + execs().with_status(101) + .with_stderr("\ + error: no matching package named `bar` found + location searched: registry `[..]` + required by package `foo v0.1.0` + ... which is depended on by `transitive_load_test v0.0.1 ([..]/transitive_load_test)` + As a reminder, you're using offline mode (-Z offline) \ + which can sometimes cause surprising resolution failures, \ + if this error is too confusing you may with to retry \ + without the offline flag.")); + } + + #[test] + fn compile_path_dep_then_change_version() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#) + .file("src/lib.rs", "") + .file("bar/Cargo.toml", r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + "#) + .file("bar/src/lib.rs", "") + .build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + + File::create(&p.root().join("bar/Cargo.toml")).unwrap().write_all(br#" + [package] + name = "bar" + version = "0.0.2" + authors = [] + "#).unwrap(); + + assert_that(p.cargo("build"), + execs().with_status(101).with_stderr("\ + error: no matching version `= 0.0.1` found for package `bar` + location searched: [..] + versions found: 0.0.2 + required by package `foo v0.0.1 ([..]/foo)` + consider running `cargo update` to update a path dependency's locked version + ")); + } + + #[test] + fn ignores_carriage_return_in_lockfile() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + "#) + .file("src/main.rs", r#" + mod a; fn main() {} + "#) + .file("src/a.rs", "") + .build(); + + assert_that(p.cargo("build"), + execs().with_status(0)); + + let lockfile = p.root().join("Cargo.lock"); + let mut lock = String::new(); + File::open(&lockfile).unwrap().read_to_string(&mut lock).unwrap(); + let lock = lock.replace("\n", "\r\n"); + File::create(&lockfile).unwrap().write_all(lock.as_bytes()).unwrap(); + assert_that(p.cargo("build"), + execs().with_status(0)); + } + + #[test] + fn cargo_default_env_metadata_env_var() { + // Ensure that path dep + dylib + env_var get metadata + // (even though path_dep + dylib should not) + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#) + .file("src/lib.rs", "// hi") + .file("bar/Cargo.toml", r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + + [lib] + name = "bar" + crate_type = ["dylib"] + "#) + .file("bar/src/lib.rs", "// hello") + .build(); + + // No metadata on libbar since it's a dylib path dependency + assert_that(p.cargo("build").arg("-v"), + execs().with_status(0).with_stderr(&format!("\ + [COMPILING] bar v0.0.1 ({url}/bar) + [RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type dylib \ + --emit=dep-info,link \ + -C prefer-dynamic -C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency={dir}[/]target[/]debug[/]deps` + [COMPILING] foo v0.0.1 ({url}) + [RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib \ + --emit=dep-info,link -C debuginfo=2 \ + -C metadata=[..] \ + -C extra-filename=[..] \ + --out-dir [..] \ + -L dependency={dir}[/]target[/]debug[/]deps \ + --extern bar={dir}[/]target[/]debug[/]deps[/]{prefix}bar{suffix}` + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]", + dir = p.root().display(), + url = p.url(), + prefix = env::consts::DLL_PREFIX, + suffix = env::consts::DLL_SUFFIX, + ))); + + assert_that(p.cargo("clean"), execs().with_status(0)); + + // If you set the env-var, then we expect metadata on libbar + assert_that(p.cargo("build").arg("-v").env("__CARGO_DEFAULT_LIB_METADATA", "stable"), + execs().with_status(0).with_stderr(&format!("\ + [COMPILING] bar v0.0.1 ({url}/bar) + [RUNNING] `rustc --crate-name bar bar[/]src[/]lib.rs --crate-type dylib \ + --emit=dep-info,link \ + -C prefer-dynamic -C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency={dir}[/]target[/]debug[/]deps` + [COMPILING] foo v0.0.1 ({url}) + [RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib \ + --emit=dep-info,link -C debuginfo=2 \ + -C metadata=[..] \ + -C extra-filename=[..] \ + --out-dir [..] \ + -L dependency={dir}[/]target[/]debug[/]deps \ + --extern bar={dir}[/]target[/]debug[/]deps[/]{prefix}bar-[..]{suffix}` + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] + ", + dir = p.root().display(), + url = p.url(), + prefix = env::consts::DLL_PREFIX, + suffix = env::consts::DLL_SUFFIX, + ))); + } + + #[test] + fn crate_env_vars() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.5.1-alpha.1" + description = "This is foo" + homepage = "http://example.com" + authors = ["wycats@example.com"] + "#) + .file("src/main.rs", r#" + extern crate foo; + + + static VERSION_MAJOR: &'static str = env!("CARGO_PKG_VERSION_MAJOR"); + static VERSION_MINOR: &'static str = env!("CARGO_PKG_VERSION_MINOR"); + static VERSION_PATCH: &'static str = env!("CARGO_PKG_VERSION_PATCH"); + static VERSION_PRE: &'static str = env!("CARGO_PKG_VERSION_PRE"); + static VERSION: &'static str = env!("CARGO_PKG_VERSION"); + static CARGO_MANIFEST_DIR: &'static str = env!("CARGO_MANIFEST_DIR"); + static PKG_NAME: &'static str = env!("CARGO_PKG_NAME"); + static HOMEPAGE: &'static str = env!("CARGO_PKG_HOMEPAGE"); + static DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION"); + + fn main() { + let s = format!("{}-{}-{} @ {} in {}", VERSION_MAJOR, + VERSION_MINOR, VERSION_PATCH, VERSION_PRE, + CARGO_MANIFEST_DIR); + assert_eq!(s, foo::version()); + println!("{}", s); + assert_eq!("foo", PKG_NAME); + assert_eq!("http://example.com", HOMEPAGE); + assert_eq!("This is foo", DESCRIPTION); + let s = format!("{}.{}.{}-{}", VERSION_MAJOR, + VERSION_MINOR, VERSION_PATCH, VERSION_PRE); + assert_eq!(s, VERSION); + } + "#) + .file("src/lib.rs", r#" + pub fn version() -> String { + format!("{}-{}-{} @ {} in {}", + env!("CARGO_PKG_VERSION_MAJOR"), + env!("CARGO_PKG_VERSION_MINOR"), + env!("CARGO_PKG_VERSION_PATCH"), + env!("CARGO_PKG_VERSION_PRE"), + env!("CARGO_MANIFEST_DIR")) + } + "#) + .build(); + + println!("build"); + assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); + + println!("bin"); + assert_that(process(&p.bin("foo")), + execs().with_status(0).with_stdout(&format!("0-5-1 @ alpha.1 in {}\n", + p.root().display()))); + + println!("test"); + assert_that(p.cargo("test").arg("-v"), + execs().with_status(0)); + } + + #[test] + fn crate_authors_env_vars() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.5.1-alpha.1" + authors = ["wycats@example.com", "neikos@example.com"] + "#) + .file("src/main.rs", r#" + extern crate foo; + + static AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS"); + + fn main() { + let s = "wycats@example.com:neikos@example.com"; + assert_eq!(AUTHORS, foo::authors()); + println!("{}", AUTHORS); + assert_eq!(s, AUTHORS); + } + "#) + .file("src/lib.rs", r#" + pub fn authors() -> String { + format!("{}", env!("CARGO_PKG_AUTHORS")) + } + "#) + .build(); + + println!("build"); + assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); + + println!("bin"); + assert_that(process(&p.bin("foo")), + execs().with_status(0).with_stdout("wycats@example.com:neikos@example.com")); + + println!("test"); + assert_that(p.cargo("test").arg("-v"), + execs().with_status(0)); + } + + // The tester may already have LD_LIBRARY_PATH=::/foo/bar which leads to a false positive error + fn setenv_for_removing_empty_component(mut p: ProcessBuilder) -> ProcessBuilder { + let v = dylib_path_envvar(); + if let Ok(search_path) = env::var(v) { + let new_search_path = + env::join_paths(env::split_paths(&search_path).filter(|e| !e.as_os_str().is_empty())) + .expect("join_paths"); + p.env(v, new_search_path); // build_command() will override LD_LIBRARY_PATH accordingly + } + p + } + + // Regression test for #4277 + #[test] + fn crate_library_path_env_var() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", &format!(r##" + fn main() {{ + let search_path = env!("{}"); + let paths = std::env::split_paths(&search_path).collect::>(); + assert!(!paths.contains(&"".into())); + }} + "##, dylib_path_envvar())) + .build(); + + assert_that(setenv_for_removing_empty_component(p.cargo("run")), + execs().with_status(0)); + } + + // Regression test for #4277 + #[test] + fn build_with_fake_libc_not_loading() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", r#" + fn main() {} + "#) + .file("src/lib.rs", r#" "#) + .file("libc.so.6", r#""#) + .build(); + + assert_that(setenv_for_removing_empty_component(p.cargo("build")), + execs().with_status(0)); + } + + // this is testing that src/.rs still works (for now) + #[test] + fn many_crate_types_old_style_lib_location() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [lib] + + name = "foo" + crate_type = ["rlib", "dylib"] + "#) + .file("src/foo.rs", r#" + pub fn foo() {} + "#) + .build(); + assert_that(p.cargo("build"), execs().with_status(0).with_stderr_contains("\ + [WARNING] path `[..]src[/]foo.rs` was erroneously implicitly accepted for library `foo`, + please rename the file to `src/lib.rs` or set lib.path in Cargo.toml")); + + assert_that(&p.root().join("target/debug/libfoo.rlib"), existing_file()); + let fname = format!("{}foo{}", env::consts::DLL_PREFIX, + env::consts::DLL_SUFFIX); + assert_that(&p.root().join("target/debug").join(&fname), existing_file()); + } + + #[test] + fn many_crate_types_correct() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [lib] + + name = "foo" + crate_type = ["rlib", "dylib"] + "#) + .file("src/lib.rs", r#" + pub fn foo() {} + "#) + .build(); + assert_that(p.cargo("build"), + execs().with_status(0)); + + assert_that(&p.root().join("target/debug/libfoo.rlib"), existing_file()); + let fname = format!("{}foo{}", env::consts::DLL_PREFIX, + env::consts::DLL_SUFFIX); + assert_that(&p.root().join("target/debug").join(&fname), existing_file()); + } + + #[test] + fn self_dependency() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + + [dependencies.test] + + path = "." + + [lib] + name = "test" + path = "src/test.rs" + "#) + .file("src/test.rs", "fn main() {}") + .build(); + assert_that(p.cargo("build"), + execs().with_status(101) + .with_stderr("\ + [ERROR] cyclic package dependency: package `test v0.0.0 ([..])` depends on itself + ")); + } + + #[test] + fn ignore_broken_symlinks() { + // windows and symlinks don't currently agree that well + if cfg!(windows) { return } + + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .symlink("Notafile", "bar") + .build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + assert_that(&p.bin("foo"), existing_file()); + + assert_that(process(&p.bin("foo")), + execs().with_status(0).with_stdout("i am foo\n")); + } + + #[test] + fn missing_lib_and_bin() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + "#) + .build(); + assert_that(p.cargo("build"), + execs().with_status(101) + .with_stderr("\ + [ERROR] failed to parse manifest at `[..]Cargo.toml` + + Caused by: + no targets specified in the manifest + either src/lib.rs, src/main.rs, a [lib] section, or [[bin]] section must be present\n")); + } + + #[test] + fn lto_build() { + // FIXME: currently this hits a linker bug on 32-bit MSVC + if cfg!(all(target_env = "msvc", target_pointer_width = "32")) { + return + } + + let p = project("foo") + .file("Cargo.toml", r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + + [profile.release] + lto = true + "#) + .file("src/main.rs", "fn main() {}") + .build(); + assert_that(p.cargo("build").arg("-v").arg("--release"), + execs().with_status(0).with_stderr(&format!("\ + [COMPILING] test v0.0.0 ({url}) + [RUNNING] `rustc --crate-name test src[/]main.rs --crate-type bin \ + --emit=dep-info,link \ + -C opt-level=3 \ + -C lto \ + -C metadata=[..] \ + --out-dir {dir}[/]target[/]release[/]deps \ + -L dependency={dir}[/]target[/]release[/]deps` + [FINISHED] release [optimized] target(s) in [..] + ", + dir = p.root().display(), + url = p.url(), + ))); + } + + #[test] + fn verbose_build() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + "#) + .file("src/lib.rs", "") + .build(); + assert_that(p.cargo("build").arg("-v"), + execs().with_status(0).with_stderr(&format!("\ + [COMPILING] test v0.0.0 ({url}) + [RUNNING] `rustc --crate-name test src[/]lib.rs --crate-type lib \ + --emit=dep-info,link -C debuginfo=2 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency={dir}[/]target[/]debug[/]deps` + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] + ", + dir = p.root().display(), + url = p.url(), + ))); + } + + #[test] + fn verbose_release_build() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + "#) + .file("src/lib.rs", "") + .build(); + assert_that(p.cargo("build").arg("-v").arg("--release"), + execs().with_status(0).with_stderr(&format!("\ + [COMPILING] test v0.0.0 ({url}) + [RUNNING] `rustc --crate-name test src[/]lib.rs --crate-type lib \ + --emit=dep-info,link \ + -C opt-level=3 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency={dir}[/]target[/]release[/]deps` + [FINISHED] release [optimized] target(s) in [..] + ", + dir = p.root().display(), + url = p.url(), + ))); + } + + #[test] + fn verbose_release_build_deps() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + + [dependencies.foo] + path = "foo" + "#) + .file("src/lib.rs", "") + .file("foo/Cargo.toml", r#" + [package] + + name = "foo" + version = "0.0.0" + authors = [] + + [lib] + name = "foo" + crate_type = ["dylib", "rlib"] + "#) + .file("foo/src/lib.rs", "") + .build(); + assert_that(p.cargo("build").arg("-v").arg("--release"), + execs().with_status(0).with_stderr(&format!("\ + [COMPILING] foo v0.0.0 ({url}/foo) + [RUNNING] `rustc --crate-name foo foo[/]src[/]lib.rs \ + --crate-type dylib --crate-type rlib \ + --emit=dep-info,link \ + -C prefer-dynamic \ + -C opt-level=3 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency={dir}[/]target[/]release[/]deps` + [COMPILING] test v0.0.0 ({url}) + [RUNNING] `rustc --crate-name test src[/]lib.rs --crate-type lib \ + --emit=dep-info,link \ + -C opt-level=3 \ + -C metadata=[..] \ + --out-dir [..] \ + -L dependency={dir}[/]target[/]release[/]deps \ + --extern foo={dir}[/]target[/]release[/]deps[/]{prefix}foo{suffix} \ + --extern foo={dir}[/]target[/]release[/]deps[/]libfoo.rlib` + [FINISHED] release [optimized] target(s) in [..] + ", + dir = p.root().display(), + url = p.url(), + prefix = env::consts::DLL_PREFIX, + suffix = env::consts::DLL_SUFFIX))); + } + + #[test] + fn explicit_examples() { + let p = project("world") + .file("Cargo.toml", r#" + [package] + name = "world" + version = "1.0.0" + authors = [] + + [lib] + name = "world" + path = "src/lib.rs" + + [[example]] + name = "hello" + path = "examples/ex-hello.rs" + + [[example]] + name = "goodbye" + path = "examples/ex-goodbye.rs" + "#) + .file("src/lib.rs", r#" + pub fn get_hello() -> &'static str { "Hello" } + pub fn get_goodbye() -> &'static str { "Goodbye" } + pub fn get_world() -> &'static str { "World" } + "#) + .file("examples/ex-hello.rs", r#" + extern crate world; + fn main() { println!("{}, {}!", world::get_hello(), world::get_world()); } + "#) + .file("examples/ex-goodbye.rs", r#" + extern crate world; + fn main() { println!("{}, {}!", world::get_goodbye(), world::get_world()); } + "#) + .build(); + + assert_that(p.cargo("test").arg("-v"), execs().with_status(0)); + assert_that(process(&p.bin("examples/hello")), + execs().with_status(0).with_stdout("Hello, World!\n")); + assert_that(process(&p.bin("examples/goodbye")), + execs().with_status(0).with_stdout("Goodbye, World!\n")); + } + + #[test] + fn non_existing_example() { + let p = project("world") + .file("Cargo.toml", r#" + [package] + name = "world" + version = "1.0.0" + authors = [] + + [lib] + name = "world" + path = "src/lib.rs" + + [[example]] + name = "hello" + "#) + .file("src/lib.rs", "") + .file("examples/ehlo.rs", "") + .build(); + + assert_that(p.cargo("test").arg("-v"), execs().with_status(101).with_stderr("\ + [ERROR] failed to parse manifest at `[..]` + + Caused by: + can't find `hello` example, specify example.path")); + } + + #[test] + fn non_existing_binary() { + let p = project("world") + .file("Cargo.toml", r#" + [package] + name = "world" + version = "1.0.0" + authors = [] + + [[bin]] + name = "hello" + "#) + .file("src/lib.rs", "") + .file("src/bin/ehlo.rs", "") + .build(); + + assert_that(p.cargo("build").arg("-v"), execs().with_status(101).with_stderr("\ + [ERROR] failed to parse manifest at `[..]` + + Caused by: + can't find `hello` bin, specify bin.path")); + } + + #[test] + fn legacy_binary_paths_warinigs() { + let p = project("world") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "1.0.0" + authors = [] + + [[bin]] + name = "bar" + "#) + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build").arg("-v"), execs().with_status(0).with_stderr_contains("\ + [WARNING] path `[..]src[/]main.rs` was erroneously implicitly accepted for binary `bar`, + please set bin.path in Cargo.toml")); + + let p = project("world") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "1.0.0" + authors = [] + + [[bin]] + name = "bar" + "#) + .file("src/lib.rs", "") + .file("src/bin/main.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build").arg("-v"), execs().with_status(0).with_stderr_contains("\ + [WARNING] path `[..]src[/]bin[/]main.rs` was erroneously implicitly accepted for binary `bar`, + please set bin.path in Cargo.toml")); + + let p = project("world") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "1.0.0" + authors = [] + + [[bin]] + name = "bar" + "#) + .file("src/bar.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build").arg("-v"), execs().with_status(0).with_stderr_contains("\ + [WARNING] path `[..]src[/]bar.rs` was erroneously implicitly accepted for binary `bar`, + please set bin.path in Cargo.toml")); + } + + #[test] + fn implicit_examples() { + let p = project("world") + .file("Cargo.toml", r#" + [package] + name = "world" + version = "1.0.0" + authors = [] + "#) + .file("src/lib.rs", r#" + pub fn get_hello() -> &'static str { "Hello" } + pub fn get_goodbye() -> &'static str { "Goodbye" } + pub fn get_world() -> &'static str { "World" } + "#) + .file("examples/hello.rs", r#" + extern crate world; + fn main() { + println!("{}, {}!", world::get_hello(), world::get_world()); + } + "#) + .file("examples/goodbye.rs", r#" + extern crate world; + fn main() { + println!("{}, {}!", world::get_goodbye(), world::get_world()); + } + "#) + .build(); + + assert_that(p.cargo("test"), execs().with_status(0)); + assert_that(process(&p.bin("examples/hello")), + execs().with_status(0).with_stdout("Hello, World!\n")); + assert_that(process(&p.bin("examples/goodbye")), + execs().with_status(0).with_stdout("Goodbye, World!\n")); + } + + #[test] + fn standard_build_no_ndebug() { + let p = project("world") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", r#" + fn main() { + if cfg!(debug_assertions) { + println!("slow") + } else { + println!("fast") + } + } + "#) + .build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + assert_that(process(&p.bin("foo")), + execs().with_status(0).with_stdout("slow\n")); + } + + #[test] + fn release_build_ndebug() { + let p = project("world") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", r#" + fn main() { + if cfg!(debug_assertions) { + println!("slow") + } else { + println!("fast") + } + } + "#) + .build(); + + assert_that(p.cargo("build").arg("--release"), + execs().with_status(0)); + assert_that(process(&p.release_bin("foo")), + execs().with_status(0).with_stdout("fast\n")); + } + + #[test] + fn inferred_main_bin() { + let p = project("world") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", r#" + fn main() {} + "#) + .build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + assert_that(process(&p.bin("foo")), execs().with_status(0)); + } + + #[test] + fn deletion_causes_failure() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.bar] + path = "bar" + "#) + .file("src/main.rs", r#" + extern crate bar; + fn main() {} + "#) + .file("bar/Cargo.toml", r#" + [package] + name = "bar" + version = "0.0.1" + authors = [] + "#) + .file("bar/src/lib.rs", "") + .build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + p.change_file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#); + assert_that(p.cargo("build"), execs().with_status(101)); + } + + #[test] + fn bad_cargo_toml_in_target_dir() { + let p = project("world") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", r#" + fn main() {} + "#) + .file("target/Cargo.toml", "bad-toml") + .build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + assert_that(process(&p.bin("foo")), execs().with_status(0)); + } + + #[test] + fn lib_with_standard_name() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "syntax" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", " + pub fn foo() {} + ") + .file("src/main.rs", " + extern crate syntax; + fn main() { syntax::foo() } + ") + .build(); + + assert_that(p.cargo("build"), + execs().with_status(0) + .with_stderr(&format!("\ + [COMPILING] syntax v0.0.1 ({dir}) + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] + ", + dir = p.url()))); + } + + #[test] + fn simple_staticlib() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + + [lib] + name = "foo" + crate-type = ["staticlib"] + "#) + .file("src/lib.rs", "pub fn foo() {}") + .build(); + + // env var is a test for #1381 + assert_that(p.cargo("build").env("RUST_LOG", "nekoneko=trace"), + execs().with_status(0)); + } + + #[test] + fn staticlib_rlib_and_bin() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + + [lib] + name = "foo" + crate-type = ["staticlib", "rlib"] + "#) + .file("src/lib.rs", "pub fn foo() {}") + .file("src/main.rs", r#" + extern crate foo; + + fn main() { + foo::foo(); + }"#) + .build(); + + assert_that(p.cargo("build").arg("-v"), execs().with_status(0)); + } + + #[test] + fn opt_out_of_bin() { + let p = project("foo") + .file("Cargo.toml", r#" + bin = [] + + [package] + name = "foo" + authors = [] + version = "0.0.1" + "#) + .file("src/lib.rs", "") + .file("src/main.rs", "bad syntax") + .build(); + assert_that(p.cargo("build"), execs().with_status(0)); + } + + #[test] + fn single_lib() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.1" + + [lib] + name = "foo" + path = "src/bar.rs" + "#) + .file("src/bar.rs", "") + .build(); + assert_that(p.cargo("build"), execs().with_status(0)); + } + + #[test] + fn freshness_ignores_excluded() { + let foo = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = "build.rs" + exclude = ["src/b*.rs"] + "#) + .file("build.rs", "fn main() {}") + .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") + .build(); + foo.root().move_into_the_past(); + + assert_that(foo.cargo("build"), + execs().with_status(0) + .with_stderr(&format!("\ + [COMPILING] foo v0.0.0 ({url}) + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] + ", url = foo.url()))); + + // Smoke test to make sure it doesn't compile again + println!("first pass"); + assert_that(foo.cargo("build"), + execs().with_status(0) + .with_stdout("")); + + // Modify an ignored file and make sure we don't rebuild + println!("second pass"); + File::create(&foo.root().join("src/bar.rs")).unwrap(); + assert_that(foo.cargo("build"), + execs().with_status(0) + .with_stdout("")); + } + + #[test] + fn rebuild_preserves_out_dir() { + let foo = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + build = 'build.rs' + "#) + .file("build.rs", r#" + use std::env; + use std::fs::File; + use std::path::Path; + + fn main() { + let path = Path::new(&env::var("OUT_DIR").unwrap()).join("foo"); + if env::var_os("FIRST").is_some() { + File::create(&path).unwrap(); + } else { + File::create(&path).unwrap(); + } + } + "#) + .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") + .build(); + foo.root().move_into_the_past(); + + assert_that(foo.cargo("build").env("FIRST", "1"), + execs().with_status(0) + .with_stderr(&format!("\ + [COMPILING] foo v0.0.0 ({url}) + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] + ", url = foo.url()))); + + File::create(&foo.root().join("src/bar.rs")).unwrap(); + assert_that(foo.cargo("build"), + execs().with_status(0) + .with_stderr(&format!("\ + [COMPILING] foo v0.0.0 ({url}) + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] + ", url = foo.url()))); + } + + #[test] + fn dep_no_libs() { + let foo = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [dependencies.bar] + path = "bar" + "#) + .file("src/lib.rs", "pub fn bar() -> i32 { 1 }") + .file("bar/Cargo.toml", r#" + [package] + name = "bar" + version = "0.0.0" + authors = [] + "#) + .file("bar/src/main.rs", "") + .build(); + assert_that(foo.cargo("build"), + execs().with_status(0)); + } + + #[test] + fn recompile_space_in_name() { + let foo = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + + [lib] + name = "foo" + path = "src/my lib.rs" + "#) + .file("src/my lib.rs", "") + .build(); + assert_that(foo.cargo("build"), execs().with_status(0)); + foo.root().move_into_the_past(); + assert_that(foo.cargo("build"), + execs().with_status(0).with_stdout("")); + } + + #[cfg(unix)] + #[test] + fn ignore_bad_directories() { + use std::os::unix::prelude::*; + let foo = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + "#) + .file("src/lib.rs", "") + .build(); + let dir = foo.root().join("tmp"); + fs::create_dir(&dir).unwrap(); + let stat = fs::metadata(&dir).unwrap(); + let mut perms = stat.permissions(); + perms.set_mode(0o644); + fs::set_permissions(&dir, perms.clone()).unwrap(); + assert_that(foo.cargo("build"), + execs().with_status(0)); + perms.set_mode(0o755); + fs::set_permissions(&dir, perms).unwrap(); + } + + #[test] + fn bad_cargo_config() { + let foo = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.0" + authors = [] + "#) + .file("src/lib.rs", "") + .file(".cargo/config", r#" + this is not valid toml + "#) + .build(); + assert_that(foo.cargo("build").arg("-v"), + execs().with_status(101).with_stderr("\ + [ERROR] Couldn't load Cargo configuration + + Caused by: + could not parse TOML configuration in `[..]` + + Caused by: + could not parse input as TOML + + Caused by: + expected an equals, found an identifier at line 2 + ")); + } + + #[test] + fn cargo_platform_specific_dependency() { + let host = rustc_host(); + let p = project("foo") + .file("Cargo.toml", &format!(r#" + [project] + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + build = "build.rs" + + [target.{host}.dependencies] + dep = {{ path = "dep" }} + [target.{host}.build-dependencies] + build = {{ path = "build" }} + [target.{host}.dev-dependencies] + dev = {{ path = "dev" }} + "#, host = host)) + .file("src/main.rs", r#" + extern crate dep; + fn main() { dep::dep() } + "#) + .file("tests/foo.rs", r#" + extern crate dev; + #[test] + fn foo() { dev::dev() } + "#) + .file("build.rs", r#" + extern crate build; + fn main() { build::build(); } + "#) + .file("dep/Cargo.toml", r#" + [project] + name = "dep" + version = "0.5.0" + authors = ["wycats@example.com"] + "#) + .file("dep/src/lib.rs", "pub fn dep() {}") + .file("build/Cargo.toml", r#" + [project] + name = "build" + version = "0.5.0" + authors = ["wycats@example.com"] + "#) + .file("build/src/lib.rs", "pub fn build() {}") + .file("dev/Cargo.toml", r#" + [project] + name = "dev" + version = "0.5.0" + authors = ["wycats@example.com"] + "#) + .file("dev/src/lib.rs", "pub fn dev() {}") + .build(); + + assert_that(p.cargo("build"), + execs().with_status(0)); + + assert_that(&p.bin("foo"), existing_file()); + assert_that(p.cargo("test"), + execs().with_status(0)); + } + + #[test] + fn bad_platform_specific_dependency() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [target.wrong-target.dependencies.bar] + path = "bar" + "#) + .file("src/main.rs", + &main_file(r#""{}", bar::gimme()"#, &["bar"])) + .file("bar/Cargo.toml", r#" + [project] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + "#) + .file("bar/src/lib.rs", r#" + extern crate baz; + + pub fn gimme() -> String { + format!("") + } + "#) + .build(); + + assert_that(p.cargo("build"), + execs().with_status(101)); + } + + #[test] + fn cargo_platform_specific_dependency_wrong_platform() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [target.non-existing-triplet.dependencies.bar] + path = "bar" + "#) + .file("src/main.rs", r#" + fn main() {} + "#) + .file("bar/Cargo.toml", r#" + [project] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + "#) + .file("bar/src/lib.rs", r#" + invalid rust file, should not be compiled + "#) + .build(); + + p.cargo("build").exec_with_output().unwrap(); + + assert_that(&p.bin("foo"), existing_file()); + assert_that(process(&p.bin("foo")), + execs().with_status(0)); + + let loc = p.root().join("Cargo.lock"); + let mut lockfile = String::new(); + File::open(&loc).unwrap().read_to_string(&mut lockfile).unwrap(); + assert!(lockfile.contains("bar")) + } + + #[test] + fn example_as_lib() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + crate-type = ["lib"] + "#) + .file("src/lib.rs", "") + .file("examples/ex.rs", "") + .build(); + + assert_that(p.cargo("build").arg("--example=ex"), execs().with_status(0)); + assert_that(&p.example_lib("ex", "lib"), existing_file()); + } + + #[test] + fn example_as_rlib() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + crate-type = ["rlib"] + "#) + .file("src/lib.rs", "") + .file("examples/ex.rs", "") + .build(); + + assert_that(p.cargo("build").arg("--example=ex"), execs().with_status(0)); + assert_that(&p.example_lib("ex", "rlib"), existing_file()); + } + + #[test] + fn example_as_dylib() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + crate-type = ["dylib"] + "#) + .file("src/lib.rs", "") + .file("examples/ex.rs", "") + .build(); + + assert_that(p.cargo("build").arg("--example=ex"), execs().with_status(0)); + assert_that(&p.example_lib("ex", "dylib"), existing_file()); + } + + #[test] + fn example_as_proc_macro() { + if !is_nightly() { + return; + } + + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [[example]] + name = "ex" + crate-type = ["proc-macro"] + "#) + .file("src/lib.rs", "") + .file("examples/ex.rs", "#![feature(proc_macro)]") + .build(); + + assert_that(p.cargo("build").arg("--example=ex"), execs().with_status(0)); + assert_that(&p.example_lib("ex", "proc-macro"), existing_file()); + } + + #[test] + fn example_bin_same_name() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .file("examples/foo.rs", "fn main() {}") + .build(); + + p.cargo("test").arg("--no-run").arg("-v") + .exec_with_output() + .unwrap(); + + assert_that(&p.bin("foo"), is_not(existing_file())); + // We expect a file of the form bin/foo-{metadata_hash} + assert_that(&p.bin("examples/foo"), existing_file()); + + p.cargo("test").arg("--no-run").arg("-v") + .exec_with_output() + .unwrap(); + + assert_that(&p.bin("foo"), is_not(existing_file())); + // We expect a file of the form bin/foo-{metadata_hash} + assert_that(&p.bin("examples/foo"), existing_file()); + } + + #[test] + fn compile_then_delete() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("run").arg("-v"), execs().with_status(0)); + assert_that(&p.bin("foo"), existing_file()); + if cfg!(windows) { + // On windows unlinking immediately after running often fails, so sleep + sleep_ms(100); + } + fs::remove_file(&p.bin("foo")).unwrap(); + assert_that(p.cargo("run").arg("-v"), + execs().with_status(0)); + } + + #[test] + fn transitive_dependencies_not_available() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.aaaaa] + path = "a" + "#) + .file("src/main.rs", "extern crate bbbbb; extern crate aaaaa; fn main() {}") + .file("a/Cargo.toml", r#" + [package] + name = "aaaaa" + version = "0.0.1" + authors = [] + + [dependencies.bbbbb] + path = "../b" + "#) + .file("a/src/lib.rs", "extern crate bbbbb;") + .file("b/Cargo.toml", r#" + [package] + name = "bbbbb" + version = "0.0.1" + authors = [] + "#) + .file("b/src/lib.rs", "") + .build(); + + assert_that(p.cargo("build").arg("-v"), + execs().with_status(101) + .with_stderr_contains("\ + [..] can't find crate for `bbbbb`[..] + ")); + } + + #[test] + fn cyclic_deps_rejected() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.a] + path = "a" + "#) + .file("src/lib.rs", "") + .file("a/Cargo.toml", r#" + [package] + name = "a" + version = "0.0.1" + authors = [] + + [dependencies.foo] + path = ".." + "#) + .file("a/src/lib.rs", "") + .build(); + + assert_that(p.cargo("build").arg("-v"), + execs().with_status(101) + .with_stderr("\ + [ERROR] cyclic package dependency: package `a v0.0.1 ([..])` depends on itself + ")); + } + + #[test] + fn predictable_filenames() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "foo" + crate-type = ["dylib", "rlib"] + "#) + .file("src/lib.rs", "") + .build(); + + assert_that(p.cargo("build").arg("-v"), + execs().with_status(0)); + assert_that(&p.root().join("target/debug/libfoo.rlib"), existing_file()); + let dylib_name = format!("{}foo{}", env::consts::DLL_PREFIX, + env::consts::DLL_SUFFIX); + assert_that(&p.root().join("target/debug").join(dylib_name), + existing_file()); + } + + #[test] + fn dashes_to_underscores() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo-bar" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", "") + .file("src/main.rs", "extern crate foo_bar; fn main() {}") + .build(); + + assert_that(p.cargo("build").arg("-v"), + execs().with_status(0)); + assert_that(&p.bin("foo-bar"), existing_file()); + } + + #[test] + fn dashes_in_crate_name_bad() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [lib] + name = "foo-bar" + "#) + .file("src/lib.rs", "") + .file("src/main.rs", "extern crate foo_bar; fn main() {}") + .build(); + + assert_that(p.cargo("build").arg("-v"), + execs().with_status(101)); + } + + #[test] + fn rustc_env_var() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", "") + .build(); + + assert_that(p.cargo("build") + .env("RUSTC", "rustc-that-does-not-exist").arg("-v"), + execs().with_status(101) + .with_stderr("\ + [ERROR] could not execute process `rustc-that-does-not-exist -vV` ([..]) + + Caused by: + [..] + ")); + assert_that(&p.bin("a"), is_not(existing_file())); + } + + #[test] + fn filtering() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("src/bin/b.rs", "fn main() {}") + .file("examples/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build").arg("--lib"), + execs().with_status(0)); + assert_that(&p.bin("a"), is_not(existing_file())); + + assert_that(p.cargo("build").arg("--bin=a").arg("--example=a"), + execs().with_status(0)); + assert_that(&p.bin("a"), existing_file()); + assert_that(&p.bin("b"), is_not(existing_file())); + assert_that(&p.bin("examples/a"), existing_file()); + assert_that(&p.bin("examples/b"), is_not(existing_file())); + } + + #[test] + fn filtering_implicit_bins() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("src/bin/b.rs", "fn main() {}") + .file("examples/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build").arg("--bins"), + execs().with_status(0)); + assert_that(&p.bin("a"), existing_file()); + assert_that(&p.bin("b"), existing_file()); + assert_that(&p.bin("examples/a"), is_not(existing_file())); + assert_that(&p.bin("examples/b"), is_not(existing_file())); + } + + #[test] + fn filtering_implicit_examples() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("src/bin/b.rs", "fn main() {}") + .file("examples/a.rs", "fn main() {}") + .file("examples/b.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build").arg("--examples"), + execs().with_status(0)); + assert_that(&p.bin("a"), is_not(existing_file())); + assert_that(&p.bin("b"), is_not(existing_file())); + assert_that(&p.bin("examples/a"), existing_file()); + assert_that(&p.bin("examples/b"), existing_file()); + } + + #[test] + fn ignore_dotfile() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/bin/.a.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build"), + execs().with_status(0)); + } + + #[test] + fn ignore_dotdirs() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/bin/a.rs", "fn main() {}") + .file(".git/Cargo.toml", "") + .file(".pc/dummy-fix.patch/Cargo.toml", "") + .build(); + + assert_that(p.cargo("build"), + execs().with_status(0)); + } + + #[test] + fn dotdir_root() { + let p = ProjectBuilder::new("foo", root().join(".foo")) + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/bin/a.rs", "fn main() {}") + .build(); + assert_that(p.cargo("build"), + execs().with_status(0)); + } + + + #[test] + fn custom_target_dir() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + let exe_name = format!("foo{}", env::consts::EXE_SUFFIX); + + assert_that(p.cargo("build").env("CARGO_TARGET_DIR", "foo/target"), + execs().with_status(0)); + assert_that(&p.root().join("foo/target/debug").join(&exe_name), + existing_file()); + assert_that(&p.root().join("target/debug").join(&exe_name), + is_not(existing_file())); + + assert_that(p.cargo("build"), + execs().with_status(0)); + assert_that(&p.root().join("foo/target/debug").join(&exe_name), + existing_file()); + assert_that(&p.root().join("target/debug").join(&exe_name), + existing_file()); + + fs::create_dir(p.root().join(".cargo")).unwrap(); + File::create(p.root().join(".cargo/config")).unwrap().write_all(br#" + [build] + target-dir = "foo/target" + "#).unwrap(); + assert_that(p.cargo("build").env("CARGO_TARGET_DIR", "bar/target"), + execs().with_status(0)); + assert_that(&p.root().join("bar/target/debug").join(&exe_name), + existing_file()); + assert_that(&p.root().join("foo/target/debug").join(&exe_name), + existing_file()); + assert_that(&p.root().join("target/debug").join(&exe_name), + existing_file()); + } + + #[test] + fn rustc_no_trans() { + if !is_nightly() { return } + + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("rustc").arg("-v").arg("--").arg("-Zno-trans"), + execs().with_status(0)); + } + + #[test] + fn build_multiple_packages() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.d1] + path = "d1" + [dependencies.d2] + path = "d2" + + [[bin]] + name = "foo" + "#) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .file("d1/Cargo.toml", r#" + [package] + name = "d1" + version = "0.0.1" + authors = [] + + [[bin]] + name = "d1" + "#) + .file("d1/src/lib.rs", "") + .file("d1/src/main.rs", "fn main() { println!(\"d1\"); }") + .file("d2/Cargo.toml", r#" + [package] + name = "d2" + version = "0.0.1" + authors = [] + + [[bin]] + name = "d2" + doctest = false + "#) + .file("d2/src/main.rs", "fn main() { println!(\"d2\"); }") + .build(); + + assert_that(p.cargo("build").arg("-p").arg("d1").arg("-p").arg("d2") + .arg("-p").arg("foo"), + execs().with_status(0)); + + assert_that(&p.bin("foo"), existing_file()); + assert_that(process(&p.bin("foo")), + execs().with_status(0).with_stdout("i am foo\n")); + + let d1_path = &p.build_dir().join("debug") + .join(format!("d1{}", env::consts::EXE_SUFFIX)); + let d2_path = &p.build_dir().join("debug") + .join(format!("d2{}", env::consts::EXE_SUFFIX)); + + assert_that(d1_path, existing_file()); + assert_that(process(d1_path), execs().with_status(0).with_stdout("d1")); + + assert_that(d2_path, existing_file()); + assert_that(process(d2_path), + execs().with_status(0).with_stdout("d2")); + } + + #[test] + fn invalid_spec() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies.d1] + path = "d1" + + [[bin]] + name = "foo" + "#) + .file("src/bin/foo.rs", &main_file(r#""i am foo""#, &[])) + .file("d1/Cargo.toml", r#" + [package] + name = "d1" + version = "0.0.1" + authors = [] + + [[bin]] + name = "d1" + "#) + .file("d1/src/lib.rs", "") + .file("d1/src/main.rs", "fn main() { println!(\"d1\"); }") + .build(); + + assert_that(p.cargo("build").arg("-p").arg("notAValidDep"), + execs().with_status(101).with_stderr("\ + [ERROR] package id specification `notAValidDep` matched no packages + ")); + + assert_that(p.cargo("build").arg("-p").arg("d1").arg("-p").arg("notAValidDep"), + execs().with_status(101).with_stderr("\ + [ERROR] package id specification `notAValidDep` matched no packages + ")); + } + + #[test] + fn manifest_with_bom_is_ok() { + let p = project("foo") + .file("Cargo.toml", "\u{FEFF} + [package] + name = \"foo\" + version = \"0.0.1\" + authors = [] + ") + .file("src/lib.rs", "") + .build(); + assert_that(p.cargo("build").arg("-v"), + execs().with_status(0)); + } + + #[test] + fn panic_abort_compiles_with_panic_abort() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [profile.dev] + panic = 'abort' + "#) + .file("src/lib.rs", "") + .build(); + assert_that(p.cargo("build").arg("-v"), + execs().with_status(0) + .with_stderr_contains("[..] -C panic=abort [..]")); + } + + #[test] + fn explicit_color_config_is_propagated_to_rustc() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + + name = "test" + version = "0.0.0" + authors = [] + "#) + .file("src/lib.rs", "") + .build(); + assert_that(p.cargo("build").arg("-v").arg("--color").arg("always"), + execs().with_status(0).with_stderr_contains( + "[..]rustc [..] src[/]lib.rs --color always[..]")); + + assert_that(p.cargo("clean"), execs().with_status(0)); + + assert_that(p.cargo("build").arg("-v").arg("--color").arg("never"), + execs().with_status(0).with_stderr("\ + [COMPILING] test v0.0.0 ([..]) + [RUNNING] `rustc [..] --color never [..]` + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] + ")); + } + + #[test] + fn compiler_json_error_format() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [profile.dev] + debug = false # prevent the *.dSYM from affecting the test result + + [dependencies.bar] + path = "bar" + "#) + .file("src/main.rs", "fn main() { let unused = 92; }") + .file("bar/Cargo.toml", r#" + [project] + + name = "bar" + version = "0.5.0" + authors = ["wycats@example.com"] + "#) + .file("bar/src/lib.rs", r#"fn dead() {}"#) + .build(); + + assert_that(p.cargo("build").arg("-v") + .arg("--message-format").arg("json"), + execs().with_status(0).with_json(r#" + { + "reason":"compiler-message", + "package_id":"bar 0.5.0 ([..])", + "target":{ + "kind":["lib"], + "crate_types":["lib"], + "name":"bar", + "src_path":"[..]lib.rs" + }, + "message":"{...}" + } + + { + "reason":"compiler-artifact", + "profile": { + "debug_assertions": true, + "debuginfo": null, + "opt_level": "0", + "overflow_checks": true, + "test": false + }, + "features": [], + "package_id":"bar 0.5.0 ([..])", + "target":{ + "kind":["lib"], + "crate_types":["lib"], + "name":"bar", + "src_path":"[..]lib.rs" + }, + "filenames":["[..].rlib"], + "fresh": false + } + + { + "reason":"compiler-message", + "package_id":"foo 0.5.0 ([..])", + "target":{ + "kind":["bin"], + "crate_types":["bin"], + "name":"foo", + "src_path":"[..]main.rs" + }, + "message":"{...}" + } + + { + "reason":"compiler-artifact", + "package_id":"foo 0.5.0 ([..])", + "target":{ + "kind":["bin"], + "crate_types":["bin"], + "name":"foo", + "src_path":"[..]main.rs" + }, + "profile": { + "debug_assertions": true, + "debuginfo": null, + "opt_level": "0", + "overflow_checks": true, + "test": false + }, + "features": [], + "filenames": ["[..]"], + "fresh": false + } + "#)); + + // With fresh build, we should repeat the artifacts, + // but omit compiler warnings. + assert_that(p.cargo("build").arg("-v") + .arg("--message-format").arg("json"), + execs().with_status(0).with_json(r#" + { + "reason":"compiler-artifact", + "profile": { + "debug_assertions": true, + "debuginfo": null, + "opt_level": "0", + "overflow_checks": true, + "test": false + }, + "features": [], + "package_id":"bar 0.5.0 ([..])", + "target":{ + "kind":["lib"], + "crate_types":["lib"], + "name":"bar", + "src_path":"[..]lib.rs" + }, + "filenames":["[..].rlib"], + "fresh": true + } + + { + "reason":"compiler-artifact", + "package_id":"foo 0.5.0 ([..])", + "target":{ + "kind":["bin"], + "crate_types":["bin"], + "name":"foo", + "src_path":"[..]main.rs" + }, + "profile": { + "debug_assertions": true, + "debuginfo": null, + "opt_level": "0", + "overflow_checks": true, + "test": false + }, + "features": [], + "filenames": ["[..]"], + "fresh": true + } + "#)); + } + + #[test] + fn wrong_message_format_option() { + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build").arg("--message-format").arg("XML"), + execs().with_status(1) + .with_stderr_contains( + r#"[ERROR] Could not match 'xml' with any of the allowed variants: ["Human", "Json"]"#)); + } + + #[test] + fn message_format_json_forward_stderr() { + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() { let unused = 0; }") + .build(); + + assert_that(p.cargo("rustc").arg("--release").arg("--bin").arg("foo") + .arg("--message-format").arg("JSON"), + execs().with_status(0) + .with_json(r#" + { + "reason":"compiler-message", + "package_id":"foo 0.5.0 ([..])", + "target":{ + "kind":["bin"], + "crate_types":["bin"], + "name":"foo", + "src_path":"[..]" + }, + "message":"{...}" + } + + { + "reason":"compiler-artifact", + "package_id":"foo 0.5.0 ([..])", + "target":{ + "kind":["bin"], + "crate_types":["bin"], + "name":"foo", + "src_path":"[..]" + }, + "profile":{ + "debug_assertions":false, + "debuginfo":null, + "opt_level":"3", + "overflow_checks": false, + "test":false + }, + "features":[], + "filenames":["[..]"], + "fresh": false + } + "#)); + } + + #[test] + fn no_warn_about_package_metadata() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + + [package.metadata] + foo = "bar" + a = true + b = 3 + + [package.metadata.another] + bar = 3 + "#) + .file("src/lib.rs", "") + .build(); + assert_that(p.cargo("build"), + execs().with_status(0) + .with_stderr("[..] foo v0.0.1 ([..])\n\ + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]\n")); + } + + #[test] + fn cargo_build_empty_target() { + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build").arg("--target").arg(""), + execs().with_status(101) + .with_stderr_contains("[..] target was empty")); + } + + #[test] + fn build_all_workspace() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = "bar" } + + [workspace] + "#) + .file("src/main.rs", r#" + fn main() {} + "#) + .file("bar/Cargo.toml", r#" + [project] + name = "bar" + version = "0.1.0" + "#) + .file("bar/src/lib.rs", r#" + pub fn bar() {} + "#) + .build(); + + assert_that(p.cargo("build") + .arg("--all"), + execs().with_status(0) + .with_stderr("[..] Compiling bar v0.1.0 ([..])\n\ + [..] Compiling foo v0.1.0 ([..])\n\ + [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n")); + } + + #[test] + fn build_all_exclude() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + + [workspace] + members = ["bar", "baz"] + "#) + .file("src/main.rs", r#" + fn main() {} + "#) + .file("bar/Cargo.toml", r#" + [project] + name = "bar" + version = "0.1.0" + "#) + .file("bar/src/lib.rs", r#" + pub fn bar() {} + "#) + .file("baz/Cargo.toml", r#" + [project] + name = "baz" + version = "0.1.0" + "#) + .file("baz/src/lib.rs", r#" + pub fn baz() { + break_the_build(); + } + "#) + .build(); + + assert_that(p.cargo("build") + .arg("--all") + .arg("--exclude") + .arg("baz"), + execs().with_status(0) + .with_stderr_contains("[..]Compiling foo v0.1.0 [..]") + .with_stderr_contains("[..]Compiling bar v0.1.0 [..]") + .with_stderr_does_not_contain("[..]Compiling baz v0.1.0 [..]")); + } + + #[test] + fn build_all_workspace_implicit_examples() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = "bar" } + + [workspace] + "#) + .file("src/lib.rs", "") + .file("src/bin/a.rs", "fn main() {}") + .file("src/bin/b.rs", "fn main() {}") + .file("examples/c.rs", "fn main() {}") + .file("examples/d.rs", "fn main() {}") + .file("bar/Cargo.toml", r#" + [project] + name = "bar" + version = "0.1.0" + "#) + .file("bar/src/lib.rs", "") + .file("bar/src/bin/e.rs", "fn main() {}") + .file("bar/src/bin/f.rs", "fn main() {}") + .file("bar/examples/g.rs", "fn main() {}") + .file("bar/examples/h.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build") + .arg("--all").arg("--examples"), + execs().with_status(0) + .with_stderr("[..] Compiling bar v0.1.0 ([..])\n\ + [..] Compiling foo v0.1.0 ([..])\n\ + [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n")); + assert_that(&p.bin("a"), is_not(existing_file())); + assert_that(&p.bin("b"), is_not(existing_file())); + assert_that(&p.bin("examples/c"), existing_file()); + assert_that(&p.bin("examples/d"), existing_file()); + assert_that(&p.bin("e"), is_not(existing_file())); + assert_that(&p.bin("f"), is_not(existing_file())); + assert_that(&p.bin("examples/g"), existing_file()); + assert_that(&p.bin("examples/h"), existing_file()); + } + + #[test] + fn build_all_virtual_manifest() { + let p = project("workspace") + .file("Cargo.toml", r#" + [workspace] + members = ["foo", "bar"] + "#) + .file("foo/Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + "#) + .file("foo/src/lib.rs", r#" + pub fn foo() {} + "#) + .file("bar/Cargo.toml", r#" + [project] + name = "bar" + version = "0.1.0" + "#) + .file("bar/src/lib.rs", r#" + pub fn bar() {} + "#) + .build(); + + // The order in which foo and bar are built is not guaranteed + assert_that(p.cargo("build") + .arg("--all"), + execs().with_status(0) + .with_stderr_contains("[..] Compiling bar v0.1.0 ([..])") + .with_stderr_contains("[..] Compiling foo v0.1.0 ([..])") + .with_stderr("[..] Compiling [..] v0.1.0 ([..])\n\ + [..] Compiling [..] v0.1.0 ([..])\n\ + [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n")); + } + + #[test] + fn build_virtual_manifest_all_implied() { + let p = project("workspace") + .file("Cargo.toml", r#" + [workspace] + members = ["foo", "bar"] + "#) + .file("foo/Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + "#) + .file("foo/src/lib.rs", r#" + pub fn foo() {} + "#) + .file("bar/Cargo.toml", r#" + [project] + name = "bar" + version = "0.1.0" + "#) + .file("bar/src/lib.rs", r#" + pub fn bar() {} + "#) + .build(); + + // The order in which foo and bar are built is not guaranteed + assert_that(p.cargo("build"), + execs().with_status(0) + .with_stderr_contains("[..] Compiling bar v0.1.0 ([..])") + .with_stderr_contains("[..] Compiling foo v0.1.0 ([..])") + .with_stderr("[..] Compiling [..] v0.1.0 ([..])\n\ + [..] Compiling [..] v0.1.0 ([..])\n\ + [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n")); + } + + #[test] + fn build_virtual_manifest_one_project() { + let p = project("workspace") + .file("Cargo.toml", r#" + [workspace] + members = ["foo", "bar"] + "#) + .file("foo/Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + "#) + .file("foo/src/lib.rs", r#" + pub fn foo() {} + "#) + .file("bar/Cargo.toml", r#" + [project] + name = "bar" + version = "0.1.0" + "#) + .file("bar/src/lib.rs", r#" + pub fn bar() {} + "#) + .build(); + + assert_that(p.cargo("build") + .arg("-p").arg("foo"), + execs().with_status(0) + .with_stderr_does_not_contain("bar") + .with_stderr_contains("[..] Compiling foo v0.1.0 ([..])") + .with_stderr("[..] Compiling [..] v0.1.0 ([..])\n\ + [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n")); + } + + #[test] + fn build_all_virtual_manifest_implicit_examples() { + let p = project("foo") + .file("Cargo.toml", r#" + [workspace] + members = ["foo", "bar"] + "#) + .file("foo/Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + "#) + .file("foo/src/lib.rs", "") + .file("foo/src/bin/a.rs", "fn main() {}") + .file("foo/src/bin/b.rs", "fn main() {}") + .file("foo/examples/c.rs", "fn main() {}") + .file("foo/examples/d.rs", "fn main() {}") + .file("bar/Cargo.toml", r#" + [project] + name = "bar" + version = "0.1.0" + "#) + .file("bar/src/lib.rs", "") + .file("bar/src/bin/e.rs", "fn main() {}") + .file("bar/src/bin/f.rs", "fn main() {}") + .file("bar/examples/g.rs", "fn main() {}") + .file("bar/examples/h.rs", "fn main() {}") + .build(); + + // The order in which foo and bar are built is not guaranteed + assert_that(p.cargo("build") + .arg("--all").arg("--examples"), + execs().with_status(0) + .with_stderr_contains("[..] Compiling bar v0.1.0 ([..])") + .with_stderr_contains("[..] Compiling foo v0.1.0 ([..])") + .with_stderr("[..] Compiling [..] v0.1.0 ([..])\n\ + [..] Compiling [..] v0.1.0 ([..])\n\ + [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n")); + assert_that(&p.bin("a"), is_not(existing_file())); + assert_that(&p.bin("b"), is_not(existing_file())); + assert_that(&p.bin("examples/c"), existing_file()); + assert_that(&p.bin("examples/d"), existing_file()); + assert_that(&p.bin("e"), is_not(existing_file())); + assert_that(&p.bin("f"), is_not(existing_file())); + assert_that(&p.bin("examples/g"), existing_file()); + assert_that(&p.bin("examples/h"), existing_file()); + } + + #[test] + fn build_all_member_dependency_same_name() { + let p = project("workspace") + .file("Cargo.toml", r#" + [workspace] + members = ["a"] + "#) + .file("a/Cargo.toml", r#" + [project] + name = "a" + version = "0.1.0" + + [dependencies] + a = "0.1.0" + "#) + .file("a/src/lib.rs", r#" + pub fn a() {} + "#) + .build(); + + Package::new("a", "0.1.0").publish(); + + assert_that(p.cargo("build") + .arg("--all"), + execs().with_status(0) + .with_stderr("[..] Updating registry `[..]`\n\ + [..] Downloading a v0.1.0 ([..])\n\ + [..] Compiling a v0.1.0\n\ + [..] Compiling a v0.1.0 ([..])\n\ + [..] Finished dev [unoptimized + debuginfo] target(s) in [..]\n")); + } + + #[test] + fn run_proper_binary() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + [[bin]] + name = "main" + [[bin]] + name = "other" + "#) + .file("src/lib.rs", "") + .file("src/bin/main.rs", r#" + fn main() { + panic!("This should never be run."); + } + "#) + .file("src/bin/other.rs", r#" + fn main() { + } + "#) + .build(); + + assert_that(p.cargo("run").arg("--bin").arg("other"), + execs().with_status(0)); + } + + #[test] + fn run_proper_binary_main_rs() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + [[bin]] + name = "foo" + "#) + .file("src/lib.rs", "") + .file("src/bin/main.rs", r#" + fn main() { + } + "#) + .build(); + + assert_that(p.cargo("run").arg("--bin").arg("foo"), + execs().with_status(0)); + } + + #[test] + fn run_proper_alias_binary_from_src() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + [[bin]] + name = "foo" + [[bin]] + name = "bar" + "#) + .file("src/foo.rs", r#" + fn main() { + println!("foo"); + } + "#).file("src/bar.rs", r#" + fn main() { + println!("bar"); + } + "#) + .build(); + + assert_that(p.cargo("build") + .arg("--all"), + execs().with_status(0) + ); + assert_that(process(&p.bin("foo")), + execs().with_status(0).with_stdout("foo\n")); + assert_that(process(&p.bin("bar")), + execs().with_status(0).with_stdout("bar\n")); + } + + #[test] + fn run_proper_alias_binary_main_rs() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + [[bin]] + name = "foo" + [[bin]] + name = "bar" + "#) + .file("src/main.rs", r#" + fn main() { + println!("main"); + } + "#) + .build(); + + assert_that(p.cargo("build") + .arg("--all"), + execs().with_status(0) + ); + assert_that(process(&p.bin("foo")), + execs().with_status(0).with_stdout("main\n")); + assert_that(process(&p.bin("bar")), + execs().with_status(0).with_stdout("main\n")); + } + + #[test] + fn run_proper_binary_main_rs_as_foo() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + [[bin]] + name = "foo" + "#) + .file("src/foo.rs", r#" + fn main() { + panic!("This should never be run."); + } + "#) + .file("src/main.rs", r#" + fn main() { + } + "#) + .build(); + + assert_that(p.cargo("run").arg("--bin").arg("foo"), + execs().with_status(0)); + } + + #[test] + fn rustc_wrapper() { + // We don't have /usr/bin/env on Windows. + if cfg!(windows) { return } + + let p = project("foo") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + assert_that(p.cargo("build").arg("-v").env("RUSTC_WRAPPER", "/usr/bin/env"), + execs().with_stderr_contains( + "[RUNNING] `/usr/bin/env rustc --crate-name foo [..]") + .with_status(0)); + } + + #[test] + fn cdylib_not_lifted() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + authors = [] + version = "0.1.0" + + [lib] + crate-type = ["cdylib"] + "#) + .file("src/lib.rs", "") + .build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + + let files = if cfg!(windows) { + vec!["foo.dll.lib", "foo.dll.exp", "foo.dll"] + } else if cfg!(target_os = "macos") { + vec!["libfoo.dylib"] + } else { + vec!["libfoo.so"] + }; + + for file in files { + println!("checking: {}", file); + assert_that(&p.root().join("target/debug/deps").join(&file), + existing_file()); + } + } + + #[test] + fn cdylib_final_outputs() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo-bar" + authors = [] + version = "0.1.0" + + [lib] + crate-type = ["cdylib"] + "#) + .file("src/lib.rs", "") + .build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + + let files = if cfg!(windows) { + vec!["foo_bar.dll.lib", "foo_bar.dll"] + } else if cfg!(target_os = "macos") { + vec!["libfoo_bar.dylib"] + } else { + vec!["libfoo_bar.so"] + }; + + for file in files { + println!("checking: {}", file); + assert_that(&p.root().join("target/debug").join(&file), existing_file()); + } + } + + #[test] + fn deterministic_cfg_flags() { + // This bug is non-deterministic + + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + authors = [] + build = "build.rs" + + [features] + default = ["f_a", "f_b", "f_c", "f_d"] + f_a = [] + f_b = [] + f_c = [] + f_d = [] + "#) + .file("build.rs", r#" + fn main() { + println!("cargo:rustc-cfg=cfg_a"); + println!("cargo:rustc-cfg=cfg_b"); + println!("cargo:rustc-cfg=cfg_c"); + println!("cargo:rustc-cfg=cfg_d"); + println!("cargo:rustc-cfg=cfg_e"); + } + "#) + .file("src/main.rs", r#" + fn main() {} + "#) + .build(); + + assert_that(p.cargo("build").arg("-v"), + execs().with_status(0) + .with_stderr("\ + [COMPILING] foo v0.1.0 [..] + [RUNNING] [..] + [RUNNING] [..] + [RUNNING] `rustc --crate-name foo [..] \ + --cfg[..]default[..]--cfg[..]f_a[..]--cfg[..]f_b[..]\ + --cfg[..]f_c[..]--cfg[..]f_d[..] \ + --cfg cfg_a --cfg cfg_b --cfg cfg_c --cfg cfg_d --cfg cfg_e` + [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]")); + } + + #[test] + fn explicit_bins_without_paths() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [[bin]] + name = "foo" + + [[bin]] + name = "bar" + "#) + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .file("src/bin/bar.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + } + + #[test] + fn no_bin_in_src_with_lib() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [[bin]] + name = "foo" + "#) + .file("src/lib.rs", "") + .file("src/foo.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build"), + execs().with_status(101) + .with_stderr_contains("\ + [ERROR] failed to parse manifest at `[..]` + + Caused by: + can't find `foo` bin, specify bin.path")); + } + + + #[test] + fn inferred_bins() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .file("src/bin/bar.rs", "fn main() {}") + .file("src/bin/baz/main.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + assert_that(&p.bin("foo"), existing_file()); + assert_that(&p.bin("bar"), existing_file()); + assert_that(&p.bin("baz"), existing_file()); + } + + #[test] + fn inferred_bins_duplicate_name() { + // this should fail, because we have two binaries with the same name + let p = project("bar") + .file("Cargo.toml", r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .file("src/bin/foo.rs", "fn main() {}") + .file("src/bin/foo/main.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build"), + execs().with_status(101) + .with_stderr_contains("\ + [..]found duplicate binary name foo, but all binary targets must have a unique name[..] + ")); + } + + #[test] + fn inferred_bin_path() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [[bin]] + name = "bar" + # Note, no `path` key! + "#) + .file("src/bin/bar/main.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + assert_that(&p.bin("bar"), existing_file()); + } + + #[test] + fn inferred_examples() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/lib.rs", "fn main() {}") + .file("examples/bar.rs", "fn main() {}") + .file("examples/baz/main.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("test"), execs().with_status(0)); + assert_that(&p.bin("examples/bar"), existing_file()); + assert_that(&p.bin("examples/baz"), existing_file()); + } + + #[test] + fn inferred_tests() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/lib.rs", "fn main() {}") + .file("tests/bar.rs", "fn main() {}") + .file("tests/baz/main.rs", "fn main() {}") + .build(); + + assert_that( + p.cargo("test").arg("--test=bar").arg("--test=baz"), + execs().with_status(0)); + } + + #[test] + fn inferred_benchmarks() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/lib.rs", "fn main() {}") + .file("benches/bar.rs", "fn main() {}") + .file("benches/baz/main.rs", "fn main() {}") + .build(); + + assert_that( + p.cargo("bench").arg("--bench=bar").arg("--bench=baz"), + execs().with_status(0)); + } + + #[test] + fn same_metadata_different_directory() { + // A top-level crate built in two different workspaces should have the + // same metadata hash. + let p = project("foo1") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + let output = t!(String::from_utf8( + t!(p.cargo("build").arg("-v").exec_with_output()) + .stderr, + )); + let metadata = output + .split_whitespace() + .find(|arg| arg.starts_with("metadata=")) + .unwrap(); + + let p = project("foo2") + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/foo.rs", &main_file(r#""i am foo""#, &[])) + .build(); + + assert_that( + p.cargo("build").arg("-v"), + execs().with_status(0).with_stderr_contains( + format!("[..]{}[..]", metadata), + ), + ); + } + + #[test] + fn building_a_dependent_crate_witout_bin_should_fail() { + Package::new("testless", "0.1.0") + .file("Cargo.toml", r#" + [project] + name = "testless" + version = "0.1.0" + + [[bin]] + name = "a_bin" + "#) + .file("src/lib.rs", "") + .publish(); + + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + + [dependencies] + testless = "0.1.0" + "#) + .file("src/lib.rs", "") + .build(); + + assert_that(p.cargo("build"), + execs().with_status(101).with_stderr_contains( + "[..]can't find `a_bin` bin, specify bin.path" + )); + } + + #[test] + fn uplift_dsym_of_bin_on_mac() { + if !cfg!(any(target_os = "macos", target_os = "ios")) { + return + } + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + "#) + .file("src/main.rs", "fn main() { panic!(); }") + .file("src/bin/b.rs", "fn main() { panic!(); }") + .file("examples/c.rs", "fn main() { panic!(); }") + .file("tests/d.rs", "fn main() { panic!(); }") + .build(); + + assert_that( + p.cargo("build").arg("--bins").arg("--examples").arg("--tests"), + execs().with_status(0) + ); + assert_that(&p.bin("foo.dSYM"), existing_dir()); + assert_that(&p.bin("b.dSYM"), existing_dir()); + assert!( + p.bin("b.dSYM") + .symlink_metadata() + .expect("read metadata from b.dSYM") + .file_type() + .is_symlink() + ); + assert_that(&p.bin("c.dSYM"), is_not(existing_dir())); + assert_that(&p.bin("d.dSYM"), is_not(existing_dir())); + } + + // Make sure that `cargo build` chooses the correct profile for building + // targets based on filters (assuming --profile is not specified). + #[test] + fn build_filter_infer_profile() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/lib.rs", "") + .file("src/main.rs", "fn main() {}") + .file("tests/t1.rs", "") + .file("benches/b1.rs", "") + .file("examples/ex1.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build").arg("-v"), + execs().with_status(0) + .with_stderr_contains("\ + [RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib \ + --emit=dep-info,link[..]") + .with_stderr_contains("\ + [RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin \ + --emit=dep-info,link[..]") + ); + + p.root().join("target").rm_rf(); + assert_that(p.cargo("build").arg("-v").arg("--test=t1"), + execs().with_status(0) + .with_stderr_contains("\ + [RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib \ + --emit=dep-info,link[..]") + .with_stderr_contains("\ + [RUNNING] `rustc --crate-name t1 tests[/]t1.rs --emit=dep-info,link[..]") + .with_stderr_contains("\ + [RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin \ + --emit=dep-info,link[..]") + ); + + p.root().join("target").rm_rf(); + assert_that(p.cargo("build").arg("-v").arg("--bench=b1"), + execs().with_status(0) + .with_stderr_contains("\ + [RUNNING] `rustc --crate-name foo src[/]lib.rs --crate-type lib \ + --emit=dep-info,link[..]") + .with_stderr_contains("\ + [RUNNING] `rustc --crate-name b1 benches[/]b1.rs --emit=dep-info,link \ + -C opt-level=3[..]") + .with_stderr_contains("\ + [RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin \ + --emit=dep-info,link[..]") + ); + } + + #[test] + fn all_targets_no_lib() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + assert_that(p.cargo("build").arg("-v").arg("--all-targets"), + execs().with_status(0) + // bin + .with_stderr_contains("\ + [RUNNING] `rustc --crate-name foo src[/]main.rs --crate-type bin \ + --emit=dep-info,link[..]") + // bench + .with_stderr_contains("\ + [RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,link \ + -C opt-level=3 --test [..]") + // unit test + .with_stderr_contains("\ + [RUNNING] `rustc --crate-name foo src[/]main.rs --emit=dep-info,link \ + -C debuginfo=2 --test [..]") + ); + } + + #[test] + fn no_linkable_target() { + // Issue 3169. This is currently not an error as per discussion in PR #4797 + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + [dependencies] + the_lib = { path = "the_lib" } + "#) + .file("src/main.rs", "fn main() {}") + .file("the_lib/Cargo.toml", r#" + [package] + name = "the_lib" + version = "0.1.0" + [lib] + name = "the_lib" + crate-type = ["staticlib"] + "#) + .file("the_lib/src/lib.rs", "pub fn foo() {}") + .build(); + assert_that(p.cargo("build"), + execs() + .with_status(0) + .with_stderr_contains("\ + [WARNING] The package `the_lib` provides no linkable [..] \ + while compiling `foo`. [..] in `the_lib`'s Cargo.toml. [..]")); + } ++ ++#[test] ++fn avoid_dev_deps() { ++ Package::new("foo", "1.0.0").publish(); ++ let p = project("foo") ++ .file("Cargo.toml", r#" ++ [package] ++ name = "bar" ++ version = "0.1.0" ++ authors = [] ++ ++ [dev-dependencies] ++ baz = "1.0.0" ++ "#) ++ .file("src/main.rs", "fn main() {}") ++ .build(); ++ ++ assert_that(p.cargo("build"), execs().with_status(101)); ++ assert_that(p.cargo("build").masquerade_as_nightly_cargo() ++ .arg("-Zavoid-dev-deps"), execs().with_status(0)); ++} diff --cc tests/testsuite/install.rs index 000000000,010301750..bd58c6e6c mode 000000,100644..100644 --- a/tests/testsuite/install.rs +++ b/tests/testsuite/install.rs @@@ -1,0 -1,1076 +1,1127 @@@ + use cargotest; + use std::fs::{self, File, OpenOptions}; + use std::io::prelude::*; + + use cargo::util::ProcessBuilder; ++use cargotest::ChannelChanger; + use cargotest::install::{cargo_home, has_installed_exe}; + use cargotest::support::git; + use cargotest::support::paths; + use cargotest::support::registry::Package; + use cargotest::support::{project, execs}; + use hamcrest::{assert_that, existing_dir, is_not}; + + fn cargo_process(s: &str) -> ProcessBuilder { + let mut p = cargotest::cargo_process(); + p.arg(s); + p + } + + fn pkg(name: &str, vers: &str) { + Package::new(name, vers) + .file("src/lib.rs", "") + .file("src/main.rs", &format!(" + extern crate {}; + fn main() {{}} + ", name)) + .publish(); + } + + #[test] + fn simple() { + pkg("foo", "0.0.1"); + + assert_that(cargo_process("install").arg("foo"), + execs().with_status(0).with_stderr(&format!("\ + [UPDATING] registry `[..]` + [DOWNLOADING] foo v0.0.1 (registry [..]) + [INSTALLING] foo v0.0.1 + [COMPILING] foo v0.0.1 + [FINISHED] release [optimized] target(s) in [..] + [INSTALLING] {home}[..]bin[..]foo[..] + warning: be sure to add `[..]` to your PATH to be able to run the installed binaries + ", + home = cargo_home().display()))); + assert_that(cargo_home(), has_installed_exe("foo")); + + assert_that(cargo_process("uninstall").arg("foo"), + execs().with_status(0).with_stderr(&format!("\ + [REMOVING] {home}[..]bin[..]foo[..] + ", + home = cargo_home().display()))); + assert_that(cargo_home(), is_not(has_installed_exe("foo"))); + } + + #[test] + fn multiple_pkgs() { + pkg("foo", "0.0.1"); + pkg("bar", "0.0.2"); + + assert_that(cargo_process("install").args(&["foo", "bar", "baz"]), + execs().with_status(101).with_stderr(&format!("\ + [UPDATING] registry `[..]` + [DOWNLOADING] foo v0.0.1 (registry `file://[..]`) + [INSTALLING] foo v0.0.1 + [COMPILING] foo v0.0.1 + [FINISHED] release [optimized] target(s) in [..] + [INSTALLING] {home}[..]bin[..]foo[..] + [DOWNLOADING] bar v0.0.2 (registry `file://[..]`) + [INSTALLING] bar v0.0.2 + [COMPILING] bar v0.0.2 + [FINISHED] release [optimized] target(s) in [..] + [INSTALLING] {home}[..]bin[..]bar[..] + error: could not find `baz` in registry `[..]` + [SUMMARY] Successfully installed foo, bar! Failed to install baz (see error(s) above). + warning: be sure to add `[..]` to your PATH to be able to run the installed binaries + error: some crates failed to install + ", + home = cargo_home().display()))); + assert_that(cargo_home(), has_installed_exe("foo")); + assert_that(cargo_home(), has_installed_exe("bar")); + + assert_that(cargo_process("uninstall").args(&["foo", "bar"]), + execs().with_status(0).with_stderr(&format!("\ + [REMOVING] {home}[..]bin[..]foo[..] + [REMOVING] {home}[..]bin[..]bar[..] + [SUMMARY] Successfully uninstalled foo, bar! + ", + home = cargo_home().display()))); + + assert_that(cargo_home(), is_not(has_installed_exe("foo"))); + assert_that(cargo_home(), is_not(has_installed_exe("bar"))); + } + + #[test] + fn pick_max_version() { + pkg("foo", "0.0.1"); + pkg("foo", "0.0.2"); + + assert_that(cargo_process("install").arg("foo"), + execs().with_status(0).with_stderr(&format!("\ + [UPDATING] registry `[..]` + [DOWNLOADING] foo v0.0.2 (registry [..]) + [INSTALLING] foo v0.0.2 + [COMPILING] foo v0.0.2 + [FINISHED] release [optimized] target(s) in [..] + [INSTALLING] {home}[..]bin[..]foo[..] + warning: be sure to add `[..]` to your PATH to be able to run the installed binaries + ", + home = cargo_home().display()))); + assert_that(cargo_home(), has_installed_exe("foo")); + } + + #[test] + fn missing() { + pkg("foo", "0.0.1"); + assert_that(cargo_process("install").arg("bar"), + execs().with_status(101).with_stderr("\ + [UPDATING] registry [..] + [ERROR] could not find `bar` in registry `[..]` + ")); + } + + #[test] + fn bad_version() { + pkg("foo", "0.0.1"); + assert_that(cargo_process("install").arg("foo").arg("--vers=0.2.0"), + execs().with_status(101).with_stderr("\ + [UPDATING] registry [..] + [ERROR] could not find `foo` in registry `[..]` with version `=0.2.0` + ")); + } + + #[test] + fn no_crate() { + assert_that(cargo_process("install"), + execs().with_status(101).with_stderr("\ + [ERROR] `[..]` is not a crate root; specify a crate to install [..] + + Caused by: + failed to read `[..]Cargo.toml` + + Caused by: + [..] (os error [..]) + ")); + } + + #[test] + fn install_location_precedence() { + pkg("foo", "0.0.1"); + + let root = paths::root(); + let t1 = root.join("t1"); + let t2 = root.join("t2"); + let t3 = root.join("t3"); + let t4 = cargo_home(); + + fs::create_dir(root.join(".cargo")).unwrap(); + File::create(root.join(".cargo/config")).unwrap().write_all(format!("\ + [install] + root = '{}' + ", t3.display()).as_bytes()).unwrap(); + + println!("install --root"); + + assert_that(cargo_process("install").arg("foo") + .arg("--root").arg(&t1) + .env("CARGO_INSTALL_ROOT", &t2), + execs().with_status(0)); + assert_that(&t1, has_installed_exe("foo")); + assert_that(&t2, is_not(has_installed_exe("foo"))); + + println!("install CARGO_INSTALL_ROOT"); + + assert_that(cargo_process("install").arg("foo") + .env("CARGO_INSTALL_ROOT", &t2), + execs().with_status(0)); + assert_that(&t2, has_installed_exe("foo")); + assert_that(&t3, is_not(has_installed_exe("foo"))); + + println!("install install.root"); + + assert_that(cargo_process("install").arg("foo"), + execs().with_status(0)); + assert_that(&t3, has_installed_exe("foo")); + assert_that(&t4, is_not(has_installed_exe("foo"))); + + fs::remove_file(root.join(".cargo/config")).unwrap(); + + println!("install cargo home"); + + assert_that(cargo_process("install").arg("foo"), + execs().with_status(0)); + assert_that(&t4, has_installed_exe("foo")); + } + + #[test] + fn install_path() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--path").arg(p.root()), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + assert_that(cargo_process("install").arg("--path").arg(".").cwd(p.root()), + execs().with_status(101).with_stderr("\ + [INSTALLING] foo v0.1.0 [..] + [ERROR] binary `foo[..]` already exists in destination as part of `foo v0.1.0 [..]` + Add --force to overwrite + ")); + } + + #[test] + fn multiple_crates_error() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .file("a/Cargo.toml", r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + "#) + .file("a/src/main.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--git").arg(p.url().to_string()), + execs().with_status(101).with_stderr("\ + [UPDATING] git repository [..] + [ERROR] multiple packages with binaries found: bar, foo + ")); + } + + #[test] + fn multiple_crates_select() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .file("a/Cargo.toml", r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + "#) + .file("a/src/main.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--git").arg(p.url().to_string()) + .arg("foo"), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + assert_that(cargo_home(), is_not(has_installed_exe("bar"))); + + assert_that(cargo_process("install").arg("--git").arg(p.url().to_string()) + .arg("bar"), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("bar")); + } + + #[test] + fn multiple_crates_auto_binaries() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "a" } + "#) + .file("src/main.rs", "extern crate bar; fn main() {}") + .file("a/Cargo.toml", r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + "#) + .file("a/src/lib.rs", "") + .build(); + + assert_that(cargo_process("install").arg("--path").arg(p.root()), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + } + + #[test] + fn multiple_crates_auto_examples() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "a" } + "#) + .file("src/lib.rs", "extern crate bar;") + .file("examples/foo.rs", " + extern crate bar; + extern crate foo; + fn main() {} + ") + .file("a/Cargo.toml", r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + "#) + .file("a/src/lib.rs", "") + .build(); + + assert_that(cargo_process("install").arg("--path").arg(p.root()) + .arg("--example=foo"), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + } + + #[test] + fn no_binaries_or_examples() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "a" } + "#) + .file("src/lib.rs", "") + .file("a/Cargo.toml", r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + "#) + .file("a/src/lib.rs", "") + .build(); + + assert_that(cargo_process("install").arg("--path").arg(p.root()), + execs().with_status(101).with_stderr("\ + [ERROR] no packages found with binaries or examples + ")); + } + + #[test] + fn no_binaries() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/lib.rs", "") + .file("examples/foo.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--path").arg(p.root()).arg("foo"), + execs().with_status(101).with_stderr("\ + [INSTALLING] foo [..] + [ERROR] specified package has no binaries + ")); + } + + #[test] + fn examples() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/lib.rs", "") + .file("examples/foo.rs", "extern crate foo; fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--path").arg(p.root()) + .arg("--example=foo"), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + } + + #[test] + fn install_twice() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/bin/foo-bin1.rs", "fn main() {}") + .file("src/bin/foo-bin2.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--path").arg(p.root()), + execs().with_status(0)); + assert_that(cargo_process("install").arg("--path").arg(p.root()), + execs().with_status(101).with_stderr("\ + [INSTALLING] foo v0.1.0 [..] + [ERROR] binary `foo-bin1[..]` already exists in destination as part of `foo v0.1.0 ([..])` + binary `foo-bin2[..]` already exists in destination as part of `foo v0.1.0 ([..])` + Add --force to overwrite + ")); + } + + #[test] + fn install_force() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--path").arg(p.root()), + execs().with_status(0)); + + let p = project("foo2") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.2.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--force").arg("--path").arg(p.root()), + execs().with_status(0).with_stderr(&format!("\ + [INSTALLING] foo v0.2.0 ([..]) + [COMPILING] foo v0.2.0 ([..]) + [FINISHED] release [optimized] target(s) in [..] + [REPLACING] {home}[..]bin[..]foo[..] + warning: be sure to add `[..]` to your PATH to be able to run the installed binaries + ", + home = cargo_home().display()))); + + assert_that(cargo_process("install").arg("--list"), + execs().with_status(0).with_stdout("\ + foo v0.2.0 ([..]): + foo[..] + ")); + } + + #[test] + fn install_force_partial_overlap() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/bin/foo-bin1.rs", "fn main() {}") + .file("src/bin/foo-bin2.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--path").arg(p.root()), + execs().with_status(0)); + + let p = project("foo2") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.2.0" + authors = [] + "#) + .file("src/bin/foo-bin2.rs", "fn main() {}") + .file("src/bin/foo-bin3.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--force").arg("--path").arg(p.root()), + execs().with_status(0).with_stderr(&format!("\ + [INSTALLING] foo v0.2.0 ([..]) + [COMPILING] foo v0.2.0 ([..]) + [FINISHED] release [optimized] target(s) in [..] + [INSTALLING] {home}[..]bin[..]foo-bin3[..] + [REPLACING] {home}[..]bin[..]foo-bin2[..] + warning: be sure to add `[..]` to your PATH to be able to run the installed binaries + ", + home = cargo_home().display()))); + + assert_that(cargo_process("install").arg("--list"), + execs().with_status(0).with_stdout("\ + foo v0.1.0 ([..]): + foo-bin1[..] + foo v0.2.0 ([..]): + foo-bin2[..] + foo-bin3[..] + ")); + } + + #[test] + fn install_force_bin() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/bin/foo-bin1.rs", "fn main() {}") + .file("src/bin/foo-bin2.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--path").arg(p.root()), + execs().with_status(0)); + + let p = project("foo2") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.2.0" + authors = [] + "#) + .file("src/bin/foo-bin1.rs", "fn main() {}") + .file("src/bin/foo-bin2.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--force") + .arg("--bin") + .arg("foo-bin2") + .arg("--path") + .arg(p.root()), + execs().with_status(0).with_stderr(&format!("\ + [INSTALLING] foo v0.2.0 ([..]) + [COMPILING] foo v0.2.0 ([..]) + [FINISHED] release [optimized] target(s) in [..] + [REPLACING] {home}[..]bin[..]foo-bin2[..] + warning: be sure to add `[..]` to your PATH to be able to run the installed binaries + ", + home = cargo_home().display()))); + + assert_that(cargo_process("install").arg("--list"), + execs().with_status(0).with_stdout("\ + foo v0.1.0 ([..]): + foo-bin1[..] + foo v0.2.0 ([..]): + foo-bin2[..] + ")); + } + + #[test] + fn compile_failure() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "") + .build(); + + assert_that(cargo_process("install").arg("--path").arg(p.root()), + execs().with_status(101).with_stderr_contains("\ + [ERROR] failed to compile `foo v0.1.0 ([..])`, intermediate artifacts can be \ + found at `[..]target` + + Caused by: + Could not compile `foo`. + + To learn more, run the command again with --verbose. + ")); + } + + #[test] + fn git_repo() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + // use `--locked` to test that we don't even try to write a lockfile + assert_that(cargo_process("install").arg("--locked").arg("--git").arg(p.url().to_string()), + execs().with_status(0).with_stderr(&format!("\ + [UPDATING] git repository `[..]` + [INSTALLING] foo v0.1.0 ([..]) + [COMPILING] foo v0.1.0 ([..]) + [FINISHED] release [optimized] target(s) in [..] + [INSTALLING] {home}[..]bin[..]foo[..] + warning: be sure to add `[..]` to your PATH to be able to run the installed binaries + ", + home = cargo_home().display()))); + assert_that(cargo_home(), has_installed_exe("foo")); + assert_that(cargo_home(), has_installed_exe("foo")); + } + + #[test] + fn list() { + pkg("foo", "0.0.1"); + pkg("bar", "0.2.1"); + pkg("bar", "0.2.2"); + + assert_that(cargo_process("install").arg("--list"), + execs().with_status(0).with_stdout("")); + + assert_that(cargo_process("install").arg("bar").arg("--vers").arg("=0.2.1"), + execs().with_status(0)); + assert_that(cargo_process("install").arg("foo"), + execs().with_status(0)); + assert_that(cargo_process("install").arg("--list"), + execs().with_status(0).with_stdout("\ + bar v0.2.1: + bar[..] + foo v0.0.1: + foo[..] + ")); + } + + #[test] + fn list_error() { + pkg("foo", "0.0.1"); + assert_that(cargo_process("install").arg("foo"), + execs().with_status(0)); + assert_that(cargo_process("install").arg("--list"), + execs().with_status(0).with_stdout("\ + foo v0.0.1: + foo[..] + ")); + let mut worldfile_path = cargo_home(); + worldfile_path.push(".crates.toml"); + let mut worldfile = OpenOptions::new() + .write(true) + .open(worldfile_path) + .expect(".crates.toml should be there"); + worldfile.write_all(b"\x00").unwrap(); + drop(worldfile); + assert_that(cargo_process("install").arg("--list").arg("--verbose"), + execs().with_status(101).with_stderr("\ + [ERROR] failed to parse crate metadata at `[..]` + + Caused by: + invalid TOML found for metadata + + Caused by: + unexpected character[..] + ")); + } + + #[test] + fn uninstall_pkg_does_not_exist() { + assert_that(cargo_process("uninstall").arg("foo"), + execs().with_status(101).with_stderr("\ + [ERROR] package id specification `foo` matched no packages + ")); + } + + #[test] + fn uninstall_bin_does_not_exist() { + pkg("foo", "0.0.1"); + + assert_that(cargo_process("install").arg("foo"), + execs().with_status(0)); + assert_that(cargo_process("uninstall").arg("foo").arg("--bin=bar"), + execs().with_status(101).with_stderr("\ + [ERROR] binary `bar[..]` not installed as part of `foo v0.0.1` + ")); + } + + #[test] + fn uninstall_piecemeal() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/bin/foo.rs", "fn main() {}") + .file("src/bin/bar.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("--path").arg(p.root()), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + assert_that(cargo_home(), has_installed_exe("bar")); + + assert_that(cargo_process("uninstall").arg("foo").arg("--bin=bar"), + execs().with_status(0).with_stderr("\ + [REMOVING] [..]bar[..] + ")); + + assert_that(cargo_home(), has_installed_exe("foo")); + assert_that(cargo_home(), is_not(has_installed_exe("bar"))); + + assert_that(cargo_process("uninstall").arg("foo").arg("--bin=foo"), + execs().with_status(0).with_stderr("\ + [REMOVING] [..]foo[..] + ")); + assert_that(cargo_home(), is_not(has_installed_exe("foo"))); + + assert_that(cargo_process("uninstall").arg("foo"), + execs().with_status(101).with_stderr("\ + [ERROR] package id specification `foo` matched no packages + ")); + } + + #[test] + fn subcommand_works_out_of_the_box() { + Package::new("cargo-foo", "1.0.0") + .file("src/main.rs", r#" + fn main() { + println!("bar"); + } + "#) + .publish(); + assert_that(cargo_process("install").arg("cargo-foo"), + execs().with_status(0)); + assert_that(cargo_process("foo"), + execs().with_status(0).with_stdout("bar\n")); + assert_that(cargo_process("--list"), + execs().with_status(0).with_stdout_contains(" foo\n")); + } + + #[test] + fn installs_from_cwd_by_default() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").cwd(p.root()), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + } + + #[test] + fn do_not_rebuilds_on_local_install() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(p.cargo("build").arg("--release"), + execs().with_status(0)); + assert_that(cargo_process("install").arg("--path").arg(p.root()), + execs().with_status(0).with_stderr("[INSTALLING] [..] + [FINISHED] release [optimized] target(s) in [..] + [INSTALLING] [..] + warning: be sure to add `[..]` to your PATH to be able to run the installed binaries + ")); + + assert!(p.build_dir().exists()); + assert!(p.release_bin("foo").exists()); + assert_that(cargo_home(), has_installed_exe("foo")); + } + + #[test] + fn reports_unsuccessful_subcommand_result() { + Package::new("cargo-fail", "1.0.0") + .file("src/main.rs", r#" + fn main() { + panic!(); + } + "#) + .publish(); + assert_that(cargo_process("install").arg("cargo-fail"), + execs().with_status(0)); + assert_that(cargo_process("--list"), + execs().with_status(0).with_stdout_contains(" fail\n")); + assert_that(cargo_process("fail"), + execs().with_status(101).with_stderr_contains("\ + thread '[..]' panicked at 'explicit panic', [..] + ")); + } + + #[test] + fn git_with_lockfile() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + + [dependencies] + bar = { path = "bar" } + "#) + .file("src/main.rs", "fn main() {}") + .file("bar/Cargo.toml", r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + "#) + .file("bar/src/lib.rs", "fn main() {}") + .file("Cargo.lock", r#" + [[package]] + name = "foo" + version = "0.1.0" + dependencies = [ "bar 0.1.0" ] + + [[package]] + name = "bar" + version = "0.1.0" + "#) + .build(); + + assert_that(cargo_process("install").arg("--git").arg(p.url().to_string()), + execs().with_status(0)); + } + + #[test] + fn q_silences_warnings() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install").arg("-q").arg("--path").arg(p.root()), + execs().with_status(0).with_stderr("")); + } + + #[test] + fn readonly_dir() { + pkg("foo", "0.0.1"); + + let root = paths::root(); + let dir = &root.join("readonly"); + fs::create_dir(root.join("readonly")).unwrap(); + let mut perms = fs::metadata(dir).unwrap().permissions(); + perms.set_readonly(true); + fs::set_permissions(dir, perms).unwrap(); + + assert_that(cargo_process("install").arg("foo").cwd(dir), + execs().with_status(0)); + assert_that(cargo_home(), has_installed_exe("foo")); + } + + #[test] + fn use_path_workspace() { + Package::new("foo", "1.0.0").publish(); + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "bar" + version = "0.1.0" + authors = [] + + [workspace] + members = ["baz"] + "#) + .file("src/main.rs", "fn main() {}") + .file("baz/Cargo.toml", r#" + [package] + name = "baz" + version = "0.1.0" + authors = [] + + [dependencies] + foo = "1" + "#) + .file("baz/src/lib.rs", "") + .build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + let lock = p.read_lockfile(); + assert_that(p.cargo("install"), execs().with_status(0)); + let lock2 = p.read_lockfile(); + assert_eq!(lock, lock2, "different lockfiles"); + } + ++#[test] ++fn dev_dependencies_no_check() { ++ Package::new("foo", "1.0.0").publish(); ++ let p = project("foo") ++ .file("Cargo.toml", r#" ++ [package] ++ name = "bar" ++ version = "0.1.0" ++ authors = [] ++ ++ [dev-dependencies] ++ baz = "1.0.0" ++ "#) ++ .file("src/main.rs", "fn main() {}") ++ .build(); ++ ++ assert_that(p.cargo("build"), execs().with_status(101)); ++ assert_that(p.cargo("install"), execs().with_status(0)); ++} ++ ++#[test] ++fn dev_dependencies_lock_file_untouched() { ++ Package::new("foo", "1.0.0").publish(); ++ let p = project("foo") ++ .file("Cargo.toml", r#" ++ [package] ++ name = "foo" ++ version = "0.1.0" ++ authors = [] ++ ++ [dev-dependencies] ++ bar = { path = "a" } ++ "#) ++ .file("src/main.rs", "fn main() {}") ++ .file("a/Cargo.toml", r#" ++ [package] ++ name = "bar" ++ version = "0.1.0" ++ authors = [] ++ "#) ++ .file("a/src/lib.rs", "") ++ .build(); ++ ++ assert_that(p.cargo("build"), execs().with_status(0)); ++ let lock = p.read_lockfile(); ++ assert_that(p.cargo("install"), execs().with_status(0)); ++ let lock2 = p.read_lockfile(); ++ assert!(lock == lock2, "different lockfiles"); ++} ++ + #[test] + fn vers_precise() { + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + + assert_that(cargo_process("install").arg("foo").arg("--vers").arg("0.1.1"), + execs().with_status(0).with_stderr_contains("\ + [DOWNLOADING] foo v0.1.1 (registry [..]) + ")); + } + + #[test] + fn version_too() { + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + + assert_that(cargo_process("install").arg("foo").arg("--version").arg("0.1.1"), + execs().with_status(0).with_stderr_contains("\ + [DOWNLOADING] foo v0.1.1 (registry [..]) + ")); + } + + #[test] + fn not_both_vers_and_version() { + pkg("foo", "0.1.1"); + pkg("foo", "0.1.2"); + + assert_that(cargo_process("install").arg("foo").arg("--version").arg("0.1.1").arg("--vers").arg("0.1.2"), + execs().with_status(101).with_stderr_contains("\ + error: invalid arguments + ")); + } + + #[test] + fn legacy_version_requirement() { + pkg("foo", "0.1.1"); + + assert_that(cargo_process("install").arg("foo").arg("--vers").arg("0.1"), + execs().with_status(0).with_stderr_contains("\ + warning: the `--vers` provided, `0.1`, is not a valid semver version + + historically Cargo treated this as a semver version requirement accidentally + and will continue to do so, but this behavior will be removed eventually + ")); + } + + #[test] + fn test_install_git_cannot_be_a_base_url() { + assert_that(cargo_process("install").arg("--git").arg("github.com:rust-lang-nursery/rustfmt.git"), + execs().with_status(101).with_stderr("\ + error: invalid url `github.com:rust-lang-nursery/rustfmt.git`: cannot-be-a-base-URLs are not supported + ")); + } + + #[test] + fn uninstall_multiple_and_specifying_bin() { + assert_that(cargo_process("uninstall").args(&["foo", "bar"]).arg("--bin").arg("baz"), + execs().with_status(101).with_stderr("\ + error: A binary can only be associated with a single installed package, specifying multiple specs with --bin is redundant. + ")); + } + + #[test] + fn uninstall_multiple_and_some_pkg_does_not_exist() { + pkg("foo", "0.0.1"); + + assert_that(cargo_process("install").arg("foo"), + execs().with_status(0)); + + assert_that(cargo_process("uninstall").args(&["foo", "bar"]), + execs().with_status(101).with_stderr(&format!("\ + [REMOVING] {home}[..]bin[..]foo[..] + error: package id specification `bar` matched no packages + [SUMMARY] Successfully uninstalled foo! Failed to uninstall bar (see error(s) above). + error: some packages failed to uninstall + ", + home = cargo_home().display()))); + + assert_that(cargo_home(), is_not(has_installed_exe("foo"))); + assert_that(cargo_home(), is_not(has_installed_exe("bar"))); + } + + #[test] + fn custom_target_dir_for_git_source() { + let p = git::repo(&paths::root().join("foo")) + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.1.0" + authors = [] + "#) + .file("src/main.rs", "fn main() {}") + .build(); + + assert_that(cargo_process("install") + .arg("--git").arg(p.url().to_string()), + execs().with_status(0)); + assert_that(&paths::root().join("target/release"), + is_not(existing_dir())); + + assert_that(cargo_process("install").arg("--force") + .arg("--git").arg(p.url().to_string()) + .env("CARGO_TARGET_DIR", "target"), + execs().with_status(0)); + assert_that(&paths::root().join("target/release"), + existing_dir()); + } + + #[test] + fn install_respects_lock_file() { + Package::new("bar", "0.1.0").publish(); + Package::new("bar", "0.1.1") + .file("src/lib.rs", "not rust") + .publish(); + Package::new("foo", "0.1.0") + .dep("bar", "0.1") + .file("src/lib.rs", "") + .file("src/main.rs", " + extern crate foo; + extern crate bar; + fn main() {} + ") + .file("Cargo.lock", r#" + [[package]] + name = "bar" + version = "0.1.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + + [[package]] + name = "foo" + version = "0.1.0" + dependencies = [ + "bar 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + ] + "#) + .publish(); + + assert_that(cargo_process("install").arg("foo"), + execs().with_status(0)); + } + + #[test] + fn lock_file_path_deps_ok() { + Package::new("bar", "0.1.0").publish(); + + Package::new("foo", "0.1.0") + .dep("bar", "0.1") + .file("src/lib.rs", "") + .file("src/main.rs", " + extern crate foo; + extern crate bar; + fn main() {} + ") + .file("Cargo.lock", r#" + [[package]] + name = "bar" + version = "0.1.0" + + [[package]] + name = "foo" + version = "0.1.0" + dependencies = [ + "bar 0.1.0", + ] + "#) + .publish(); + + assert_that(cargo_process("install").arg("foo"), + execs().with_status(0)); + }